|
@@ -14,14 +14,12 @@
|
|
|
14
14
|
#include <key_io.h>
|
|
15
15
|
#include <fs.h>
|
|
16
16
|
#include <policy/policy.h>
|
|
17
|
-
#include <policy/rbf.h>
|
|
18
17
|
#include <primitives/transaction.h>
|
|
19
18
|
#include <script/script.h>
|
|
20
19
|
#include <script/sign.h>
|
|
21
20
|
#include <script/signingprovider.h>
|
|
22
21
|
#include <univalue.h>
|
|
23
22
|
#include <util/moneystr.h>
|
|
24
|
-
#include <util/rbf.h>
|
|
25
23
|
#include <util/strencodings.h>
|
|
26
24
|
#include <util/string.h>
|
|
27
25
|
#include <util/system.h>
|
|
@@ -54,6 +52,7 @@ static void SetupBitcoinTxArgs(ArgsManager &argsman)
|
|
|
54
52
|
argsman.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
55
53
|
argsman.AddArg("locktime=N", "Set TX lock time to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
56
54
|
argsman.AddArg("nversion=N", "Set TX version to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
55
|
+
argsman.AddArg("ntime=N", "Set TX timestamp to N", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
57
56
|
argsman.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
58
57
|
argsman.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
59
58
|
argsman.AddArg("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", "Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS. "
|
|
@@ -65,7 +64,6 @@ static void SetupBitcoinTxArgs(ArgsManager &argsman)
|
|
|
65
64
|
argsman.AddArg("outscript=VALUE:SCRIPT[:FLAGS]", "Add raw script output to TX. "
|
|
66
65
|
"Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output. "
|
|
67
66
|
"Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
68
|
-
argsman.AddArg("replaceable(=N)", "Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)", ArgsManager::ALLOW_ANY, OptionsCategory::COMMANDS);
|
|
69
67
|
argsman.AddArg("sign=SIGHASH-FLAGS", "Add zero or more signatures to transaction. "
|
|
70
68
|
"This command requires JSON registers:"
|
|
71
69
|
"prevtxs=JSON object, "
|
|
@@ -101,14 +99,14 @@ static int AppInitRawTx(int argc, char* argv[])
|
|
|
101
99
|
|
|
102
100
|
if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) {
|
|
103
101
|
// First part of help message is specific to this utility
|
|
104
|
-
std::string strUsage = PACKAGE_NAME "
|
|
102
|
+
std::string strUsage = PACKAGE_NAME " peercoin-tx utility version " + FormatFullVersion() + "\n";
|
|
105
103
|
|
|
106
104
|
if (gArgs.IsArgSet("-version")) {
|
|
107
105
|
strUsage += FormatParagraph(LicenseInfo());
|
|
108
106
|
} else {
|
|
109
107
|
strUsage += "\n"
|
|
110
|
-
"Usage:
|
|
111
|
-
"or:
|
|
108
|
+
"Usage: peercoin-tx [options] <hex-tx> [commands] Update hex-encoded peercoin transaction\n"
|
|
109
|
+
"or: peercoin-tx [options] -create [commands] Create hex-encoded peercoin transaction\n"
|
|
112
110
|
"\n";
|
|
113
111
|
strUsage += gArgs.GetHelpMessage();
|
|
114
112
|
}
|
|
@@ -219,24 +217,13 @@ static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
|
|
|
219
217
|
tx.nLockTime = (unsigned int) newLocktime;
|
|
220
218
|
}
|
|
221
219
|
|
|
222
|
-
static void
|
|
220
|
+
static void MutateTxTime(CMutableTransaction& tx, const std::string& cmdVal)
|
|
223
221
|
{
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
throw std::runtime_error("Invalid TX input index '" + strInIdx + "'");
|
|
228
|
-
}
|
|
222
|
+
int64_t newTime;
|
|
223
|
+
if (!ParseInt64(cmdVal, &newTime) || newTime < 0LL || newTime > 0xffffffffLL)
|
|
224
|
+
throw std::runtime_error("Invalid TX time requested: '" + cmdVal + "'");
|
|
229
225
|
|
|
230
|
-
|
|
231
|
-
int cnt = 0;
|
|
232
|
-
for (CTxIn& txin : tx.vin) {
|
|
233
|
-
if (strInIdx == "" || cnt == inIdx) {
|
|
234
|
-
if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) {
|
|
235
|
-
txin.nSequence = MAX_BIP125_RBF_SEQUENCE;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
++cnt;
|
|
239
|
-
}
|
|
226
|
+
tx.nTime = (unsigned int) newTime;
|
|
240
227
|
}
|
|
241
228
|
|
|
242
229
|
template <typename T>
|
|
@@ -559,7 +546,7 @@ static CAmount AmountFromValue(const UniValue& value)
|
|
|
559
546
|
if (!value.isNum() && !value.isStr())
|
|
560
547
|
throw std::runtime_error("Amount is not a number or string");
|
|
561
548
|
CAmount amount;
|
|
562
|
-
if (!ParseFixedPoint(value.getValStr(),
|
|
549
|
+
if (!ParseFixedPoint(value.getValStr(), 6, &amount))
|
|
563
550
|
throw std::runtime_error("Invalid amount");
|
|
564
551
|
if (!MoneyRange(amount))
|
|
565
552
|
throw std::runtime_error("Amount out of range");
|
|
@@ -708,9 +695,8 @@ static void MutateTx(CMutableTransaction& tx, const std::string& command,
|
|
|
708
695
|
MutateTxVersion(tx, commandVal);
|
|
709
696
|
else if (command == "locktime")
|
|
710
697
|
MutateTxLocktime(tx, commandVal);
|
|
711
|
-
else if (command == "
|
|
712
|
-
|
|
713
|
-
}
|
|
698
|
+
else if (command == "ntime")
|
|
699
|
+
MutateTxTime(tx, commandVal);
|
|
714
700
|
|
|
715
701
|
else if (command == "delin")
|
|
716
702
|
MutateTxDelInput(tx, commandVal);
|
|
@@ -114,7 +114,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
|
|
114
114
|
|
|
115
115
|
util::ThreadSetInternalName("init");
|
|
116
116
|
|
|
117
|
-
// If Qt is used, parameters/
|
|
117
|
+
// If Qt is used, parameters/peercoin.conf are parsed in qt/bitcoin.cpp's main()
|
|
118
118
|
ArgsManager& args = *Assert(node.args);
|
|
119
119
|
SetupServerArgs(args);
|
|
120
120
|
std::string error;
|
|
@@ -129,7 +129,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
|
|
129
129
|
if (args.IsArgSet("-version")) {
|
|
130
130
|
strUsage += FormatParagraph(LicenseInfo());
|
|
131
131
|
} else {
|
|
132
|
-
strUsage += "\nUsage:
|
|
132
|
+
strUsage += "\nUsage: peercoind [options] Start " PACKAGE_NAME "\n"
|
|
133
133
|
"\n";
|
|
134
134
|
strUsage += args.GetHelpMessage();
|
|
135
135
|
}
|
|
@@ -165,7 +165,7 @@ static bool AppInit(NodeContext& node, int argc, char* argv[])
|
|
|
165
165
|
// Error out when loose non-argument tokens are encountered on command line
|
|
166
166
|
for (int i = 1; i < argc; i++) {
|
|
167
167
|
if (!IsSwitchChar(argv[i][0])) {
|
|
168
|
-
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see
|
|
168
|
+
return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see peercoind -h for a list of options.\n", argv[i])));
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
|
|
19
19
|
CBlockHeaderAndShortTxIDs::CBlockHeaderAndShortTxIDs(const CBlock& block, bool fUseWTXID) :
|
|
20
20
|
nonce(GetRand(std::numeric_limits<uint64_t>::max())),
|
|
21
|
-
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block) {
|
|
21
|
+
shorttxids(block.vtx.size() - 1), prefilledtxn(1), header(block), vchBlockSig(block.vchBlockSig) {
|
|
22
22
|
FillShortTxIDSelector();
|
|
23
23
|
//TODO: Use our mempool prior to block acceptance to predictively fill more than just the coinbase
|
|
24
24
|
prefilledtxn[0] = {0, block.vtx[0]};
|
|
25
|
+
header.nFlags = block.nFlags;
|
|
25
26
|
for (size_t i = 1; i < block.vtx.size(); i++) {
|
|
26
27
|
const CTransaction& tx = *block.vtx[i];
|
|
27
28
|
shorttxids[i - 1] = GetShortID(fUseWTXID ? tx.GetWitnessHash() : tx.GetHash());
|
|
@@ -54,6 +55,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
|
|
|
54
55
|
|
|
55
56
|
assert(header.IsNull() && txn_available.empty());
|
|
56
57
|
header = cmpctblock.header;
|
|
58
|
+
vchBlockSig = cmpctblock.vchBlockSig;
|
|
57
59
|
txn_available.resize(cmpctblock.BlockTxCount());
|
|
58
60
|
|
|
59
61
|
int32_t lastprefilledindex = -1;
|
|
@@ -177,6 +179,7 @@ ReadStatus PartiallyDownloadedBlock::FillBlock(CBlock& block, const std::vector<
|
|
|
177
179
|
assert(!header.IsNull());
|
|
178
180
|
uint256 hash = header.GetHash();
|
|
179
181
|
block = header;
|
|
182
|
+
block.vchBlockSig = vchBlockSig;
|
|
180
183
|
block.vtx.resize(txn_available.size());
|
|
181
184
|
|
|
182
185
|
size_t tx_missing_offset = 0;
|
|
@@ -100,6 +100,7 @@ public:
|
|
|
100
100
|
static constexpr int SHORTTXIDS_LENGTH = 6;
|
|
101
101
|
|
|
102
102
|
CBlockHeader header;
|
|
103
|
+
std::vector<unsigned char> vchBlockSig;
|
|
103
104
|
|
|
104
105
|
// Dummy for deserialization
|
|
105
106
|
CBlockHeaderAndShortTxIDs() {}
|
|
@@ -112,7 +113,7 @@ public:
|
|
|
112
113
|
|
|
113
114
|
SERIALIZE_METHODS(CBlockHeaderAndShortTxIDs, obj)
|
|
114
115
|
{
|
|
115
|
-
READWRITE(obj.header, obj.nonce, Using<VectorFormatter<CustomUintFormatter<SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn);
|
|
116
|
+
READWRITE(obj.header, obj.nonce, obj.vchBlockSig, Using<VectorFormatter<CustomUintFormatter<SHORTTXIDS_LENGTH>>>(obj.shorttxids), obj.prefilledtxn);
|
|
116
117
|
if (ser_action.ForRead()) {
|
|
117
118
|
if (obj.BlockTxCount() > std::numeric_limits<uint16_t>::max()) {
|
|
118
119
|
throw std::ios_base::failure("indexes overflowed 16 bits");
|
|
@@ -129,6 +130,7 @@ protected:
|
|
|
129
130
|
const CTxMemPool* pool;
|
|
130
131
|
public:
|
|
131
132
|
CBlockHeader header;
|
|
133
|
+
std::vector<unsigned char> vchBlockSig;
|
|
132
134
|
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
|
|
133
135
|
|
|
134
136
|
// extra_txn is a list of extra transactions to look at, in <witness hash, reference> form
|
|
@@ -122,7 +122,7 @@ void CBlockIndex::BuildSkip()
|
|
|
122
122
|
pskip = pprev->GetAncestor(GetSkipHeight(nHeight));
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
arith_uint256
|
|
125
|
+
arith_uint256 GetBlockTrust(const CBlockIndex& block)
|
|
126
126
|
{
|
|
127
127
|
arith_uint256 bnTarget;
|
|
128
128
|
bool fNegative;
|
|
@@ -134,20 +134,20 @@ arith_uint256 GetBlockProof(const CBlockIndex& block)
|
|
|
134
134
|
// as it's too large for an arith_uint256. However, as 2**256 is at least as large
|
|
135
135
|
// as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1,
|
|
136
136
|
// or ~bnTarget / (bnTarget+1) + 1.
|
|
137
|
-
return (~bnTarget / (bnTarget + 1)) + 1;
|
|
137
|
+
return block.IsProofOfStake() ? (~bnTarget / (bnTarget + 1)) + 1 : 1;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params& params)
|
|
141
141
|
{
|
|
142
142
|
arith_uint256 r;
|
|
143
143
|
int sign = 1;
|
|
144
|
-
if (to.
|
|
145
|
-
r = to.
|
|
144
|
+
if (to.nChainTrust > from.nChainTrust) {
|
|
145
|
+
r = to.nChainTrust - from.nChainTrust;
|
|
146
146
|
} else {
|
|
147
|
-
r = from.
|
|
147
|
+
r = from.nChainTrust - to.nChainTrust;
|
|
148
148
|
sign = -1;
|
|
149
149
|
}
|
|
150
|
-
r = r * arith_uint256(params.nPowTargetSpacing) /
|
|
150
|
+
r = r * arith_uint256(params.nPowTargetSpacing) / GetBlockTrust(tip);
|
|
151
151
|
if (r.bits() > 63) {
|
|
152
152
|
return sign * std::numeric_limits<int64_t>::max();
|
|
153
153
|
}
|
|
@@ -172,3 +172,11 @@ const CBlockIndex* LastCommonAncestor(const CBlockIndex* pa, const CBlockIndex*
|
|
|
172
172
|
assert(pa == pb);
|
|
173
173
|
return pa;
|
|
174
174
|
}
|
|
175
|
+
|
|
176
|
+
// peercoin: find last block index up to pindex
|
|
177
|
+
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake)
|
|
178
|
+
{
|
|
179
|
+
while (pindex && pindex->pprev && (pindex->IsProofOfStake() != fProofOfStake))
|
|
180
|
+
pindex = pindex->pprev;
|
|
181
|
+
return pindex;
|
|
182
|
+
}
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
#include <tinyformat.h>
|
|
15
15
|
#include <uint256.h>
|
|
16
16
|
|
|
17
|
+
#include <util/moneystr.h>
|
|
18
|
+
|
|
17
19
|
#include <vector>
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Maximum amount of time that a block timestamp is allowed to exceed the
|
|
21
23
|
* current network-adjusted time before the block will be accepted.
|
|
22
24
|
*/
|
|
23
|
-
static constexpr int64_t
|
|
25
|
+
static constexpr int64_t MAX_FUTURE_BLOCK_TIME_PREV9 = 2 * 60 * 60;
|
|
26
|
+
static constexpr int64_t MAX_FUTURE_BLOCK_TIME = 15 * 60;
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
29
|
* Timestamp window used as a grace period by code that compares external
|
|
@@ -28,7 +31,7 @@ static constexpr int64_t MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60;
|
|
|
28
31
|
* to block timestamps. This should be set at least as high as
|
|
29
32
|
* MAX_FUTURE_BLOCK_TIME.
|
|
30
33
|
*/
|
|
31
|
-
static constexpr int64_t TIMESTAMP_WINDOW =
|
|
34
|
+
static constexpr int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME_PREV9;
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* Maximum gap between node time and block time used
|
|
@@ -173,7 +176,7 @@ public:
|
|
|
173
176
|
unsigned int nUndoPos GUARDED_BY(::cs_main){0};
|
|
174
177
|
|
|
175
178
|
//! (memory only) Total amount of work (expected number of hashes) in the chain up to and including this block
|
|
176
|
-
arith_uint256
|
|
179
|
+
arith_uint256 nChainTrust{};
|
|
177
180
|
|
|
178
181
|
//! Number of transactions in this block.
|
|
179
182
|
//! Note: in a potential headers-first mode, this number cannot be relied upon
|
|
@@ -213,6 +216,66 @@ public:
|
|
|
213
216
|
//! (memory only) Maximum nTime in the chain up to and including this block.
|
|
214
217
|
unsigned int nTimeMax{0};
|
|
215
218
|
|
|
219
|
+
// peercoin
|
|
220
|
+
// peercoin: money supply related block index fields
|
|
221
|
+
int64_t nMint{0};
|
|
222
|
+
int64_t nMoneySupply{0};
|
|
223
|
+
|
|
224
|
+
// peercoin: proof-of-stake related block index fields
|
|
225
|
+
unsigned int nFlags{0}; // peercoin: block index flags
|
|
226
|
+
enum
|
|
227
|
+
{
|
|
228
|
+
BLOCK_PROOF_OF_STAKE = (1 << 0), // is proof-of-stake block
|
|
229
|
+
BLOCK_STAKE_ENTROPY = (1 << 1), // entropy bit for stake modifier
|
|
230
|
+
BLOCK_STAKE_MODIFIER = (1 << 2), // regenerated stake modifier
|
|
231
|
+
};
|
|
232
|
+
uint64_t nStakeModifier{0}; // hash modifier for proof-of-stake
|
|
233
|
+
unsigned int nStakeModifierChecksum{0}; // checksum of index; in-memeory only
|
|
234
|
+
COutPoint prevoutStake{};
|
|
235
|
+
unsigned int nStakeTime{0};
|
|
236
|
+
uint256 hashProofOfStake{};
|
|
237
|
+
|
|
238
|
+
bool IsProofOfWork() const
|
|
239
|
+
{
|
|
240
|
+
return !(nFlags & BLOCK_PROOF_OF_STAKE);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
bool IsProofOfStake() const
|
|
244
|
+
{
|
|
245
|
+
return (nFlags & BLOCK_PROOF_OF_STAKE);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
void SetProofOfStake()
|
|
249
|
+
{
|
|
250
|
+
nFlags |= BLOCK_PROOF_OF_STAKE;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
unsigned int GetStakeEntropyBit() const
|
|
254
|
+
{
|
|
255
|
+
return ((nFlags & BLOCK_STAKE_ENTROPY) >> 1);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
bool SetStakeEntropyBit(unsigned int nEntropyBit)
|
|
259
|
+
{
|
|
260
|
+
if (nEntropyBit > 1)
|
|
261
|
+
return false;
|
|
262
|
+
nFlags |= (nEntropyBit? BLOCK_STAKE_ENTROPY : 0);
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
bool GeneratedStakeModifier() const
|
|
267
|
+
{
|
|
268
|
+
return (nFlags & BLOCK_STAKE_MODIFIER);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
void SetStakeModifier(uint64_t nModifier, bool fGeneratedStakeModifier)
|
|
272
|
+
{
|
|
273
|
+
nStakeModifier = nModifier;
|
|
274
|
+
if (fGeneratedStakeModifier)
|
|
275
|
+
nFlags |= BLOCK_STAKE_MODIFIER;
|
|
276
|
+
}
|
|
277
|
+
// peercoin end
|
|
278
|
+
|
|
216
279
|
CBlockIndex()
|
|
217
280
|
{
|
|
218
281
|
}
|
|
@@ -222,7 +285,8 @@ public:
|
|
|
222
285
|
hashMerkleRoot{block.hashMerkleRoot},
|
|
223
286
|
nTime{block.nTime},
|
|
224
287
|
nBits{block.nBits},
|
|
225
|
-
nNonce{block.nNonce}
|
|
288
|
+
nNonce{block.nNonce},
|
|
289
|
+
nFlags{block.nFlags}
|
|
226
290
|
{
|
|
227
291
|
}
|
|
228
292
|
|
|
@@ -258,6 +322,7 @@ public:
|
|
|
258
322
|
block.nTime = nTime;
|
|
259
323
|
block.nBits = nBits;
|
|
260
324
|
block.nNonce = nNonce;
|
|
325
|
+
block.nFlags = nFlags;
|
|
261
326
|
return block;
|
|
262
327
|
}
|
|
263
328
|
|
|
@@ -271,7 +336,7 @@ public:
|
|
|
271
336
|
* downloaded (and stored to disk) at some point.
|
|
272
337
|
*
|
|
273
338
|
* Does not imply the transactions are consensus-valid (ConnectTip might fail)
|
|
274
|
-
* Does not imply the transactions are still stored on disk.
|
|
339
|
+
* Does not imply the transactions are still stored on disk.
|
|
275
340
|
*/
|
|
276
341
|
bool HaveTxsDownloaded() const { return nChainTx != 0; }
|
|
277
342
|
|
|
@@ -285,6 +350,32 @@ public:
|
|
|
285
350
|
return (int64_t)nTimeMax;
|
|
286
351
|
}
|
|
287
352
|
|
|
353
|
+
/**
|
|
354
|
+
* Duplicate from bitcoinrpc that originaly define this method.
|
|
355
|
+
* May require some cleanup since this method should be available both for rpc
|
|
356
|
+
* and qt clients.
|
|
357
|
+
*/
|
|
358
|
+
double GetBlockDifficulty() const
|
|
359
|
+
{
|
|
360
|
+
int nShift = (nBits >> 24) & 0xff;
|
|
361
|
+
|
|
362
|
+
double dDiff =
|
|
363
|
+
(double)0x0000ffff / (double)(nBits & 0x00ffffff);
|
|
364
|
+
|
|
365
|
+
while (nShift < 29)
|
|
366
|
+
{
|
|
367
|
+
dDiff *= 256.0;
|
|
368
|
+
nShift++;
|
|
369
|
+
}
|
|
370
|
+
while (nShift > 29)
|
|
371
|
+
{
|
|
372
|
+
dDiff /= 256.0;
|
|
373
|
+
nShift--;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return dDiff;
|
|
377
|
+
}
|
|
378
|
+
|
|
288
379
|
static constexpr int nMedianTimeSpan = 11;
|
|
289
380
|
|
|
290
381
|
int64_t GetMedianTimePast() const
|
|
@@ -303,10 +394,15 @@ public:
|
|
|
303
394
|
|
|
304
395
|
std::string ToString() const
|
|
305
396
|
{
|
|
306
|
-
return strprintf("CBlockIndex(
|
|
307
|
-
pprev, nHeight,
|
|
308
|
-
|
|
309
|
-
|
|
397
|
+
return strprintf("CBlockIndex(nprev=%08x, nFile=%d, nHeight=%d, nMint=%s, nMoneySupply=%s, nFlags=(%s)(%d)(%s), nStakeModifier=%016llx, nStakeModifierChecksum=%08x, hashProofOfStake=%s, prevoutStake=(%s), nStakeTime=%d merkle=%s, hashBlock=%s)",
|
|
398
|
+
pprev, nFile, nHeight,
|
|
399
|
+
FormatMoney(nMint), FormatMoney(nMoneySupply),
|
|
400
|
+
GeneratedStakeModifier() ? "MOD" : "-", GetStakeEntropyBit(), IsProofOfStake()? "PoS" : "PoW",
|
|
401
|
+
nStakeModifier, nStakeModifierChecksum,
|
|
402
|
+
hashProofOfStake.ToString(),
|
|
403
|
+
prevoutStake.ToString(), nStakeTime,
|
|
404
|
+
hashMerkleRoot.ToString().substr(0,10),
|
|
405
|
+
GetBlockHash().ToString().substr(0,20));
|
|
310
406
|
}
|
|
311
407
|
|
|
312
408
|
//! Check whether this block index entry is valid up to the passed validity level.
|
|
@@ -357,7 +453,7 @@ public:
|
|
|
357
453
|
const CBlockIndex* GetAncestor(int height) const;
|
|
358
454
|
};
|
|
359
455
|
|
|
360
|
-
arith_uint256
|
|
456
|
+
arith_uint256 GetBlockTrust(const CBlockIndex& block);
|
|
361
457
|
/** Return the time it would take to redo the work difference between from and to, assuming the current hashrate corresponds to the difficulty at tip, in seconds. */
|
|
362
458
|
int64_t GetBlockProofEquivalentTime(const CBlockIndex& to, const CBlockIndex& from, const CBlockIndex& tip, const Consensus::Params&);
|
|
363
459
|
/** Find the forking point between two chain tips. */
|
|
@@ -393,6 +489,17 @@ public:
|
|
|
393
489
|
if (obj.nStatus & BLOCK_HAVE_DATA) READWRITE(VARINT(obj.nDataPos));
|
|
394
490
|
if (obj.nStatus & BLOCK_HAVE_UNDO) READWRITE(VARINT(obj.nUndoPos));
|
|
395
491
|
|
|
492
|
+
READWRITE(obj.nMint);
|
|
493
|
+
READWRITE(obj.nMoneySupply);
|
|
494
|
+
READWRITE(obj.nFlags);
|
|
495
|
+
READWRITE(obj.nStakeModifier);
|
|
496
|
+
if (obj.nFlags & BLOCK_PROOF_OF_STAKE)
|
|
497
|
+
{
|
|
498
|
+
READWRITE(obj.prevoutStake);
|
|
499
|
+
READWRITE(obj.nStakeTime);
|
|
500
|
+
READWRITE(obj.hashProofOfStake);
|
|
501
|
+
}
|
|
502
|
+
|
|
396
503
|
// block header
|
|
397
504
|
READWRITE(obj.nVersion);
|
|
398
505
|
READWRITE(obj.hashPrev);
|
|
@@ -491,4 +598,6 @@ public:
|
|
|
491
598
|
CBlockIndex* FindEarliestAtLeast(int64_t nTime, int height) const;
|
|
492
599
|
};
|
|
493
600
|
|
|
601
|
+
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
|
|
602
|
+
|
|
494
603
|
#endif // BITCOIN_CHAIN_H
|
|
@@ -16,18 +16,19 @@
|
|
|
16
16
|
#include <boost/algorithm/string/classification.hpp>
|
|
17
17
|
#include <boost/algorithm/string/split.hpp>
|
|
18
18
|
|
|
19
|
-
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t
|
|
19
|
+
static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTimeTx, uint32_t nTimeBlock, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
|
|
20
20
|
{
|
|
21
21
|
CMutableTransaction txNew;
|
|
22
22
|
txNew.nVersion = 1;
|
|
23
23
|
txNew.vin.resize(1);
|
|
24
24
|
txNew.vout.resize(1);
|
|
25
|
-
txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(
|
|
25
|
+
txNew.vin[0].scriptSig = CScript() << 486604799 << CScriptNum(9999) << std::vector<unsigned char>((const unsigned char*)pszTimestamp, (const unsigned char*)pszTimestamp + strlen(pszTimestamp));
|
|
26
26
|
txNew.vout[0].nValue = genesisReward;
|
|
27
27
|
txNew.vout[0].scriptPubKey = genesisOutputScript;
|
|
28
|
+
txNew.nTime = nTimeTx;
|
|
28
29
|
|
|
29
30
|
CBlock genesis;
|
|
30
|
-
genesis.nTime =
|
|
31
|
+
genesis.nTime = nTimeBlock;
|
|
31
32
|
genesis.nBits = nBits;
|
|
32
33
|
genesis.nNonce = nNonce;
|
|
33
34
|
genesis.nVersion = nVersion;
|
|
@@ -48,11 +49,11 @@ static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesi
|
|
|
48
49
|
* CTxOut(nValue=50.00000000, scriptPubKey=0x5F1DF16B2B704C8A578D0B)
|
|
49
50
|
* vMerkleTree: 4a5e1e
|
|
50
51
|
*/
|
|
51
|
-
static CBlock CreateGenesisBlock(uint32_t
|
|
52
|
+
static CBlock CreateGenesisBlock(uint32_t nTimeTx, uint32_t nTimeBlock, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward)
|
|
52
53
|
{
|
|
53
|
-
const char* pszTimestamp = "
|
|
54
|
-
const CScript genesisOutputScript = CScript()
|
|
55
|
-
return CreateGenesisBlock(pszTimestamp, genesisOutputScript,
|
|
54
|
+
const char* pszTimestamp = "Matonis 07-AUG-2012 Parallel Currencies And The Roadmap To Monetary Freedom";
|
|
55
|
+
const CScript genesisOutputScript = CScript();
|
|
56
|
+
return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTimeTx, nTimeBlock, nNonce, nBits, nVersion, genesisReward);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/**
|
|
@@ -64,80 +65,71 @@ public:
|
|
|
64
65
|
strNetworkID = CBaseChainParams::MAIN;
|
|
65
66
|
consensus.signet_blocks = false;
|
|
66
67
|
consensus.signet_challenge.clear();
|
|
67
|
-
consensus.
|
|
68
|
-
consensus.
|
|
69
|
-
consensus.
|
|
70
|
-
consensus.
|
|
71
|
-
consensus.
|
|
72
|
-
|
|
73
|
-
consensus.
|
|
74
|
-
consensus.
|
|
75
|
-
consensus.
|
|
76
|
-
consensus.
|
|
77
|
-
consensus.
|
|
78
|
-
consensus.
|
|
68
|
+
//consensus.BIP16Height = 0;
|
|
69
|
+
consensus.BIP34Height = 339994;
|
|
70
|
+
consensus.BIP34Hash = uint256S("000000000000000237f50af4cfe8924e8693abc5bd8ae5abb95bc6d230f5953f");
|
|
71
|
+
consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 32;
|
|
72
|
+
consensus.bnInitialHashTarget = uint256S("0000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 40;
|
|
73
|
+
|
|
74
|
+
consensus.nTargetTimespan = 7 * 24 * 60 * 60; // one week
|
|
75
|
+
consensus.nStakeTargetSpacing = 10 * 60; // 10-minute block spacing
|
|
76
|
+
consensus.nTargetSpacingWorkMax = 12 * consensus.nStakeTargetSpacing; // 2-hour
|
|
77
|
+
consensus.nPowTargetSpacing = consensus.nStakeTargetSpacing;
|
|
78
|
+
consensus.nStakeMinAge = 60 * 60 * 24 * 30; // minimum age for coin age
|
|
79
|
+
consensus.nStakeMaxAge = 60 * 60 * 24 * 90;
|
|
80
|
+
consensus.nModifierInterval = 6 * 60 * 60; // Modifier interval: time to elapse before new modifier is computed
|
|
81
|
+
consensus.nCoinbaseMaturity = 500;
|
|
82
|
+
|
|
79
83
|
consensus.fPowAllowMinDifficultyBlocks = false;
|
|
80
84
|
consensus.fPowNoRetargeting = false;
|
|
81
85
|
consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
|
|
82
86
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
|
83
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
|
84
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
|
85
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
86
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
|
90
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021
|
|
91
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
|
|
92
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021
|
|
88
|
+
consensus.SegwitHeight = 455470;
|
|
93
89
|
|
|
94
|
-
consensus.nMinimumChainWork = uint256S("
|
|
95
|
-
consensus.defaultAssumeValid = uint256S("
|
|
90
|
+
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000002a0fac8b39f476"); // 350000
|
|
91
|
+
consensus.defaultAssumeValid = uint256S("0xa3a0ffa0dbca75923ad6a53d3878d62f8b35c363282df3f13ded9e4fda921e63"); // 380000
|
|
96
92
|
|
|
97
93
|
/**
|
|
98
94
|
* The message start string is designed to be unlikely to occur in normal data.
|
|
99
95
|
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
|
|
100
96
|
* a large 32-bit integer with any alignment.
|
|
101
97
|
*/
|
|
102
|
-
pchMessageStart[0] =
|
|
103
|
-
pchMessageStart[1] =
|
|
104
|
-
pchMessageStart[2] =
|
|
105
|
-
pchMessageStart[3] =
|
|
106
|
-
nDefaultPort =
|
|
98
|
+
pchMessageStart[0] = 0xe6;
|
|
99
|
+
pchMessageStart[1] = 0xe8;
|
|
100
|
+
pchMessageStart[2] = 0xe9;
|
|
101
|
+
pchMessageStart[3] = 0xe5;
|
|
102
|
+
nDefaultPort = 9901;
|
|
107
103
|
nPruneAfterHeight = 100000;
|
|
108
|
-
m_assumed_blockchain_size =
|
|
109
|
-
m_assumed_chain_state_size = 6;
|
|
104
|
+
m_assumed_blockchain_size = 1;
|
|
110
105
|
|
|
111
|
-
genesis = CreateGenesisBlock(
|
|
106
|
+
genesis = CreateGenesisBlock(1345083810, 1345084287, 2179302059u, 0x1d00ffff, 1, 0);
|
|
112
107
|
consensus.hashGenesisBlock = genesis.GetHash();
|
|
113
|
-
assert(consensus.hashGenesisBlock == uint256S("
|
|
114
|
-
assert(genesis.hashMerkleRoot == uint256S("
|
|
108
|
+
assert(consensus.hashGenesisBlock == uint256S("0x0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3"));
|
|
109
|
+
assert(genesis.hashMerkleRoot == uint256S("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
|
|
115
110
|
|
|
116
111
|
// Note that of those which support the service bits prefix, most only support a subset of
|
|
117
112
|
// possible options.
|
|
118
113
|
// This is fine at runtime as we'll fall back to using them as an addrfetch if they don't support the
|
|
119
114
|
// service bits we want, but we should get them updated to support all service bits wanted by any
|
|
120
115
|
// release ASAP to avoid it where possible.
|
|
121
|
-
vSeeds.emplace_back("seed.
|
|
122
|
-
vSeeds.emplace_back("
|
|
123
|
-
vSeeds.emplace_back("
|
|
124
|
-
vSeeds.emplace_back("seed.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
vSeeds.emplace_back("seed.bitcoin.wiz.biz."); // Jason Maurice
|
|
130
|
-
|
|
131
|
-
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,0);
|
|
132
|
-
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,5);
|
|
133
|
-
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,128);
|
|
116
|
+
vSeeds.emplace_back("seed.peercoin.net");
|
|
117
|
+
vSeeds.emplace_back("seed2.peercoin.net");
|
|
118
|
+
vSeeds.emplace_back("seed.peercoin-library.org");
|
|
119
|
+
vSeeds.emplace_back("seed.ppcoin.info");
|
|
120
|
+
|
|
121
|
+
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,55); // peercoin: addresses begin with 'P'
|
|
122
|
+
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,117); // peercoin: addresses begin with 'p'
|
|
123
|
+
base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,183);
|
|
134
124
|
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E};
|
|
135
125
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4};
|
|
136
126
|
|
|
137
|
-
|
|
127
|
+
// human readable prefix to bench32 address
|
|
128
|
+
bech32_hrp = "pc";
|
|
138
129
|
|
|
139
130
|
vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_main), std::end(chainparams_seed_main));
|
|
140
131
|
|
|
132
|
+
fMiningRequiresPeers = true;
|
|
141
133
|
fDefaultConsistencyChecks = false;
|
|
142
134
|
fRequireStandard = true;
|
|
143
135
|
m_is_test_chain = false;
|
|
@@ -145,19 +137,19 @@ public:
|
|
|
145
137
|
|
|
146
138
|
checkpointData = {
|
|
147
139
|
{
|
|
148
|
-
{
|
|
149
|
-
{
|
|
150
|
-
{
|
|
151
|
-
{
|
|
152
|
-
{
|
|
153
|
-
{
|
|
154
|
-
{
|
|
155
|
-
{
|
|
156
|
-
{
|
|
157
|
-
{
|
|
158
|
-
{
|
|
159
|
-
{
|
|
160
|
-
{
|
|
140
|
+
{ 0, uint256S("0x0000000032fe677166d54963b62a4677d8957e87c508eaa4fd7eb1c880cd27e3")},
|
|
141
|
+
{ 19080, uint256S("0x000000000000bca54d9ac17881f94193fd6a270c1bb21c3bf0b37f588a40dbd7")},
|
|
142
|
+
{ 30583, uint256S("0xd39d1481a7eecba48932ea5913be58ad3894c7ee6d5a8ba8abeb772c66a6696e")},
|
|
143
|
+
{ 99999, uint256S("0x27fd5e1de16a4270eb8c68dee2754a64da6312c7c3a0e99a7e6776246be1ee3f")},
|
|
144
|
+
{219999, uint256S("0xab0dad4b10d2370f009ed6df6effca1ba42f01d5070d6b30afeedf6463fbe7a2")},
|
|
145
|
+
{336000, uint256S("0x4d261cef6e61a5ed8325e560f1d6e36f4698853a4c7134677f47a1d1d842bdf6")},
|
|
146
|
+
{371850, uint256S("0x6b18adcb0a6e080dae85b74eee2b83fabb157bbea64fab0ed2192b2f6d5b89f3")},
|
|
147
|
+
{407813, uint256S("0x00000000000000012730b0f48bed8afbeb08164c9d63597afb082e82ea05cec9")},
|
|
148
|
+
{443561, uint256S("0xf81cea8e4e40b2cfcc13a8bd82436399c35a55df951b95e7128601c1838029ed")},
|
|
149
|
+
{455470, uint256S("0xd1472c26229f90b8589d331aa47ba9023cb953b92dce342c753e7a6b3431bf1e")},
|
|
150
|
+
{479189, uint256S("0xc9c065028b20a23fbb9627bbca5946c7497f11e1f72433d4d215c79047cf06f2")},
|
|
151
|
+
{504051, uint256S("0xff65454ebdf1d89174bec10a3c016db92f7b1d9a4759603472842f254be8d7b3")},
|
|
152
|
+
{589659, uint256S("0x967c14abf21214639aeff0a270c4543cd3b80fe53178384ac5aa3c277662f1d0")},
|
|
161
153
|
}
|
|
162
154
|
};
|
|
163
155
|
|
|
@@ -166,10 +158,12 @@ public:
|
|
|
166
158
|
};
|
|
167
159
|
|
|
168
160
|
chainTxData = ChainTxData{
|
|
169
|
-
// Data
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
161
|
+
// Data as of block 967c14abf21214639aeff0a270c4543cd3b80fe53178384ac5aa3c277662f1d0 (height 589659).
|
|
162
|
+
1635782211, // * UNIX timestamp of last known number of transactions
|
|
163
|
+
1992832, // * total number of transactions between genesis and that timestamp
|
|
164
|
+
// (the tx=... number in the ChainStateFlushed debug.log lines)
|
|
165
|
+
0.006862798 // * estimated number of transactions per second after that timestamp
|
|
166
|
+
// 1992832/(1635782211-1345400356) = 0.006862798
|
|
173
167
|
};
|
|
174
168
|
}
|
|
175
169
|
};
|
|
@@ -183,57 +177,51 @@ public:
|
|
|
183
177
|
strNetworkID = CBaseChainParams::TESTNET;
|
|
184
178
|
consensus.signet_blocks = false;
|
|
185
179
|
consensus.signet_challenge.clear();
|
|
186
|
-
consensus.
|
|
187
|
-
consensus.
|
|
188
|
-
consensus.
|
|
189
|
-
consensus.
|
|
190
|
-
consensus.
|
|
191
|
-
|
|
192
|
-
consensus.
|
|
193
|
-
consensus.
|
|
194
|
-
consensus.
|
|
195
|
-
consensus.
|
|
196
|
-
consensus.
|
|
197
|
-
consensus.
|
|
180
|
+
//consensus.BIP16Height = 0;
|
|
181
|
+
consensus.BIP34Height = 293368;
|
|
182
|
+
consensus.BIP34Hash = uint256S("00000002c0b976c7a5c9878f1cec63fb4d88d68d614aedeaf8158c42d904795e");
|
|
183
|
+
consensus.powLimit = uint256S("0000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 28;
|
|
184
|
+
consensus.bnInitialHashTarget = uint256S("00000007ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 29;
|
|
185
|
+
|
|
186
|
+
consensus.nTargetTimespan = 7 * 24 * 60 * 60; // one week
|
|
187
|
+
consensus.nStakeTargetSpacing = 10 * 60; // 10-minute block spacing
|
|
188
|
+
consensus.nTargetSpacingWorkMax = 12 * consensus.nStakeTargetSpacing; // 2-hour
|
|
189
|
+
consensus.nPowTargetSpacing = consensus.nStakeTargetSpacing;
|
|
190
|
+
consensus.nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day
|
|
191
|
+
consensus.nStakeMaxAge = 60 * 60 * 24 * 90;
|
|
192
|
+
consensus.nModifierInterval = 60 * 20; // Modifier interval: time to elapse before new modifier is computed
|
|
193
|
+
consensus.nCoinbaseMaturity = 60;
|
|
194
|
+
|
|
198
195
|
consensus.fPowAllowMinDifficultyBlocks = true;
|
|
199
196
|
consensus.fPowNoRetargeting = false;
|
|
200
197
|
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
|
|
201
198
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
|
202
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
|
203
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
|
204
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
205
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
|
206
199
|
|
|
207
|
-
|
|
208
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
|
209
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1619222400; // April 24th, 2021
|
|
210
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021
|
|
211
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
|
200
|
+
consensus.SegwitHeight = 394215;
|
|
212
201
|
|
|
213
|
-
consensus.nMinimumChainWork = uint256S("
|
|
214
|
-
consensus.defaultAssumeValid = uint256S("
|
|
202
|
+
consensus.nMinimumChainWork = uint256S("0x00");
|
|
203
|
+
consensus.defaultAssumeValid = uint256S("0x0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1"); //1135275
|
|
215
204
|
|
|
216
|
-
pchMessageStart[0] =
|
|
217
|
-
pchMessageStart[1] =
|
|
218
|
-
pchMessageStart[2] =
|
|
219
|
-
pchMessageStart[3] =
|
|
220
|
-
nDefaultPort =
|
|
205
|
+
pchMessageStart[0] = 0xcb;
|
|
206
|
+
pchMessageStart[1] = 0xf2;
|
|
207
|
+
pchMessageStart[2] = 0xc0;
|
|
208
|
+
pchMessageStart[3] = 0xef;
|
|
209
|
+
nDefaultPort = 9903;
|
|
221
210
|
nPruneAfterHeight = 1000;
|
|
222
|
-
m_assumed_blockchain_size =
|
|
223
|
-
m_assumed_chain_state_size = 2;
|
|
211
|
+
m_assumed_blockchain_size = 2;
|
|
224
212
|
|
|
225
|
-
genesis = CreateGenesisBlock(
|
|
213
|
+
genesis = CreateGenesisBlock(1345083810, 1345090000, 122894938, 0x1d0fffff, 1, 0);
|
|
226
214
|
consensus.hashGenesisBlock = genesis.GetHash();
|
|
227
|
-
assert(consensus.hashGenesisBlock == uint256S("
|
|
228
|
-
assert(genesis.hashMerkleRoot == uint256S("
|
|
215
|
+
assert(consensus.hashGenesisBlock == uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06"));
|
|
216
|
+
assert(genesis.hashMerkleRoot == uint256S("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
|
|
229
217
|
|
|
230
218
|
vFixedSeeds.clear();
|
|
231
219
|
vSeeds.clear();
|
|
232
220
|
// nodes with support for servicebits filtering should be at the top
|
|
233
|
-
vSeeds.emplace_back("
|
|
234
|
-
vSeeds.emplace_back("
|
|
235
|
-
vSeeds.emplace_back("
|
|
236
|
-
vSeeds.emplace_back("
|
|
221
|
+
vSeeds.emplace_back("tseed.peercoin.net");
|
|
222
|
+
vSeeds.emplace_back("tseed2.peercoin.net");
|
|
223
|
+
vSeeds.emplace_back("tseed.peercoin-library.org");
|
|
224
|
+
vSeeds.emplace_back("testseed.ppcoin.info");
|
|
237
225
|
|
|
238
226
|
base58Prefixes[PUBKEY_ADDRESS] = std::vector<unsigned char>(1,111);
|
|
239
227
|
base58Prefixes[SCRIPT_ADDRESS] = std::vector<unsigned char>(1,196);
|
|
@@ -241,10 +229,12 @@ public:
|
|
|
241
229
|
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
|
242
230
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
|
243
231
|
|
|
244
|
-
|
|
232
|
+
// human readable prefix to bench32 address
|
|
233
|
+
bech32_hrp = "tpc";
|
|
245
234
|
|
|
246
235
|
vFixedSeeds = std::vector<uint8_t>(std::begin(chainparams_seed_test), std::end(chainparams_seed_test));
|
|
247
236
|
|
|
237
|
+
fMiningRequiresPeers = true;
|
|
248
238
|
fDefaultConsistencyChecks = false;
|
|
249
239
|
fRequireStandard = false;
|
|
250
240
|
m_is_test_chain = true;
|
|
@@ -252,7 +242,18 @@ public:
|
|
|
252
242
|
|
|
253
243
|
checkpointData = {
|
|
254
244
|
{
|
|
255
|
-
{
|
|
245
|
+
{ 0, uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06")},
|
|
246
|
+
{ 19080, uint256S("0xb054d63d41852d71b611eaa8eca37d9fddca69b5013cf0966d453402ec8005ce")},
|
|
247
|
+
{ 30583, uint256S("0x5179c0c496b5d25ab81ffe14273ea6928c6ff81c0a0d6a83b5d7d41d64886300")},
|
|
248
|
+
{ 99999, uint256S("0xa7b03b14b8673683d972ab81775f3e85fea4fe689874b5956183466535dc651c")},
|
|
249
|
+
{219999, uint256S("0x0691bb86c92762c5c4c5a3723585ebeb7ec59310bbb0bdb6666551ab24ad919e")},
|
|
250
|
+
{336000, uint256S("0xf07adf61615c529f7c282b858d13d3e037b197324cb12e0669c461947494c4e3")},
|
|
251
|
+
{372751, uint256S("0x000000000000148db599b217c117b5104f5043c55f6ca2a8a065d9fab9f9bba1")},
|
|
252
|
+
{382019, uint256S("0x3ab75769d7957d9bf0857b5019d0a0e41044fa9ecf30b2f9c32aa457b0864ce5")},
|
|
253
|
+
{408500, uint256S("0x1636ac08b073d26b28fa40243d58dd5deb215752efe094c92c61998e4e9baf3f")},
|
|
254
|
+
{412691, uint256S("0x0e20318be88f07f521453435b37cfc516c3de07264a78ed7170985a1126126ab")},
|
|
255
|
+
{441299, uint256S("0x4091d0836a37c50ceee876000ac0cb251fd10031dab901d2c0677cd86283096e")},
|
|
256
|
+
{442735, uint256S("0x1b83b33894d51be0b8b323bfab093f638915236e0e40ba3b52bb33fdbc4053cd")},
|
|
256
257
|
}
|
|
257
258
|
};
|
|
258
259
|
|
|
@@ -261,10 +262,14 @@ public:
|
|
|
261
262
|
};
|
|
262
263
|
|
|
263
264
|
chainTxData = ChainTxData{
|
|
264
|
-
// Data
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
265
|
+
// Data as of block 0x1b83b33894d51be0b8b323bfab093f638915236e0e40ba3b52bb33fdbc4053cd (height 442735)
|
|
266
|
+
1632053274, // * UNIX timestamp of last known number of transactions
|
|
267
|
+
863997, // * total number of transactions between genesis and that timestamp
|
|
268
|
+
|
|
269
|
+
// (the tx=... number in the SetBestChain debug.log lines)
|
|
270
|
+
0.003020718 // * estimated number of transactions per second after that timestamp
|
|
271
|
+
// 863997/(1632053274-1346029522) = 0.003020718
|
|
272
|
+
|
|
268
273
|
};
|
|
269
274
|
}
|
|
270
275
|
};
|
|
@@ -289,7 +294,6 @@ public:
|
|
|
289
294
|
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000de26b0e471");
|
|
290
295
|
consensus.defaultAssumeValid = uint256S("0x00000112852484b5fe3451572368f93cfd2723279af3464e478aee35115256ef"); // 78788
|
|
291
296
|
m_assumed_blockchain_size = 1;
|
|
292
|
-
m_assumed_chain_state_size = 0;
|
|
293
297
|
chainTxData = ChainTxData{
|
|
294
298
|
// Data from RPC: getchaintxstats 4096 0000003d9144c56ac110ae04a0c271a0acce2f14f426b39fdf0d938c96d2eb09
|
|
295
299
|
/* nTime */ 1645631279,
|
|
@@ -306,7 +310,6 @@ public:
|
|
|
306
310
|
consensus.nMinimumChainWork = uint256{};
|
|
307
311
|
consensus.defaultAssumeValid = uint256{};
|
|
308
312
|
m_assumed_blockchain_size = 0;
|
|
309
|
-
m_assumed_chain_state_size = 0;
|
|
310
313
|
chainTxData = ChainTxData{
|
|
311
314
|
0,
|
|
312
315
|
0,
|
|
@@ -322,15 +325,12 @@ public:
|
|
|
322
325
|
strNetworkID = CBaseChainParams::SIGNET;
|
|
323
326
|
consensus.signet_blocks = true;
|
|
324
327
|
consensus.signet_challenge.assign(bin.begin(), bin.end());
|
|
325
|
-
consensus.nSubsidyHalvingInterval = 210000;
|
|
326
328
|
consensus.BIP16Exception = uint256{};
|
|
327
329
|
consensus.BIP34Height = 1;
|
|
328
330
|
consensus.BIP34Hash = uint256{};
|
|
329
|
-
consensus.BIP65Height = 1;
|
|
330
|
-
consensus.BIP66Height = 1;
|
|
331
331
|
consensus.CSVHeight = 1;
|
|
332
332
|
consensus.SegwitHeight = 1;
|
|
333
|
-
consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
|
333
|
+
// consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks
|
|
334
334
|
consensus.nPowTargetSpacing = 10 * 60;
|
|
335
335
|
consensus.fPowAllowMinDifficultyBlocks = false;
|
|
336
336
|
consensus.fPowNoRetargeting = false;
|
|
@@ -338,6 +338,7 @@ public:
|
|
|
338
338
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
|
339
339
|
consensus.MinBIP9WarningHeight = 0;
|
|
340
340
|
consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000");
|
|
341
|
+
/*
|
|
341
342
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
|
342
343
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
|
343
344
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
@@ -348,7 +349,7 @@ public:
|
|
|
348
349
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
|
349
350
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
350
351
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
|
351
|
-
|
|
352
|
+
*/
|
|
352
353
|
// message start is defined as the first 4 bytes of the sha256d of the block script
|
|
353
354
|
CHashWriter h(SER_DISK, 0);
|
|
354
355
|
h << consensus.signet_challenge;
|
|
@@ -358,10 +359,10 @@ public:
|
|
|
358
359
|
nDefaultPort = 38333;
|
|
359
360
|
nPruneAfterHeight = 1000;
|
|
360
361
|
|
|
361
|
-
genesis = CreateGenesisBlock(
|
|
362
|
+
genesis = CreateGenesisBlock(1345083810, 1345090000, 122894938, 0x1d0fffff, 1, 0);
|
|
362
363
|
consensus.hashGenesisBlock = genesis.GetHash();
|
|
363
|
-
assert(consensus.hashGenesisBlock == uint256S("
|
|
364
|
-
assert(genesis.hashMerkleRoot == uint256S("
|
|
364
|
+
assert(consensus.hashGenesisBlock == uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06"));
|
|
365
|
+
assert(genesis.hashMerkleRoot == uint256S("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
|
|
365
366
|
|
|
366
367
|
vFixedSeeds.clear();
|
|
367
368
|
|
|
@@ -384,70 +385,67 @@ public:
|
|
|
384
385
|
* Regression test: intended for private networks only. Has minimal difficulty to ensure that
|
|
385
386
|
* blocks can be found instantly.
|
|
386
387
|
*/
|
|
388
|
+
|
|
387
389
|
class CRegTestParams : public CChainParams {
|
|
388
390
|
public:
|
|
389
391
|
explicit CRegTestParams(const ArgsManager& args) {
|
|
390
392
|
strNetworkID = CBaseChainParams::REGTEST;
|
|
391
393
|
consensus.signet_blocks = false;
|
|
392
394
|
consensus.signet_challenge.clear();
|
|
393
|
-
consensus.nSubsidyHalvingInterval = 150;
|
|
394
395
|
consensus.BIP16Exception = uint256();
|
|
395
396
|
consensus.BIP34Height = 1; // Always active unless overridden
|
|
396
397
|
consensus.BIP34Hash = uint256();
|
|
397
|
-
consensus.
|
|
398
|
-
consensus.
|
|
399
|
-
|
|
400
|
-
consensus.
|
|
401
|
-
consensus.
|
|
402
|
-
consensus.
|
|
403
|
-
consensus.
|
|
404
|
-
|
|
398
|
+
consensus.powLimit = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 28;
|
|
399
|
+
consensus.bnInitialHashTarget = uint256S("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // ~arith_uint256(0) >> 29;
|
|
400
|
+
|
|
401
|
+
consensus.nTargetTimespan = 7 * 24 * 60 * 60; // two weeks
|
|
402
|
+
consensus.nStakeTargetSpacing = 10 * 60; // 10-minute block spacing
|
|
403
|
+
consensus.nTargetSpacingWorkMax = 12 * consensus.nStakeTargetSpacing; // 2-hour
|
|
404
|
+
consensus.nPowTargetSpacing = consensus.nStakeTargetSpacing;
|
|
405
|
+
|
|
406
|
+
consensus.nStakeMinAge = 60 * 60 * 24; // test net min age is 1 day
|
|
407
|
+
consensus.nStakeMaxAge = 60 * 60 * 24 * 90;
|
|
408
|
+
consensus.nModifierInterval = 60 * 20; // Modifier interval: time to elapse before new modifier is computed
|
|
409
|
+
consensus.nCoinbaseMaturity = 60;
|
|
410
|
+
|
|
405
411
|
consensus.fPowAllowMinDifficultyBlocks = true;
|
|
406
412
|
consensus.fPowNoRetargeting = true;
|
|
407
413
|
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains
|
|
408
414
|
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
|
|
409
|
-
|
|
410
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
|
411
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
|
|
412
|
-
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
415
|
+
/*
|
|
413
416
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
|
414
417
|
|
|
415
418
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
|
416
419
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
|
417
420
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
418
421
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
|
419
|
-
|
|
422
|
+
*/
|
|
420
423
|
consensus.nMinimumChainWork = uint256{};
|
|
421
424
|
consensus.defaultAssumeValid = uint256{};
|
|
422
425
|
|
|
423
|
-
pchMessageStart[0] =
|
|
424
|
-
pchMessageStart[1] =
|
|
425
|
-
pchMessageStart[2] =
|
|
426
|
-
pchMessageStart[3] =
|
|
427
|
-
nDefaultPort =
|
|
428
|
-
nPruneAfterHeight =
|
|
426
|
+
pchMessageStart[0] = 0xcb;
|
|
427
|
+
pchMessageStart[1] = 0xf2;
|
|
428
|
+
pchMessageStart[2] = 0xc0;
|
|
429
|
+
pchMessageStart[3] = 0xef;
|
|
430
|
+
nDefaultPort = 9903;
|
|
431
|
+
nPruneAfterHeight = 1000;
|
|
429
432
|
m_assumed_blockchain_size = 0;
|
|
430
|
-
m_assumed_chain_state_size = 0;
|
|
431
433
|
|
|
432
|
-
|
|
434
|
+
genesis = CreateGenesisBlock(1345083810, 1345090000, 122894938, 0x1d0fffff, 1, 0);
|
|
433
435
|
|
|
434
|
-
genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN);
|
|
435
436
|
consensus.hashGenesisBlock = genesis.GetHash();
|
|
436
|
-
assert(consensus.hashGenesisBlock == uint256S("
|
|
437
|
-
assert(genesis.hashMerkleRoot == uint256S("
|
|
437
|
+
assert(consensus.hashGenesisBlock == uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06"));
|
|
438
|
+
assert(genesis.hashMerkleRoot == uint256S("0x3c2d8f85fab4d17aac558cc648a1a58acff0de6deb890c29985690052c5993c2"));
|
|
438
439
|
|
|
439
440
|
vFixedSeeds.clear(); //!< Regtest mode doesn't have any fixed seeds.
|
|
440
441
|
vSeeds.clear();
|
|
441
442
|
vSeeds.emplace_back("dummySeed.invalid.");
|
|
442
443
|
|
|
443
|
-
fDefaultConsistencyChecks = true;
|
|
444
|
-
fRequireStandard = true;
|
|
445
444
|
m_is_test_chain = true;
|
|
446
445
|
m_is_mockable_chain = true;
|
|
447
|
-
|
|
448
446
|
checkpointData = {
|
|
449
447
|
{
|
|
450
|
-
{0, uint256S("
|
|
448
|
+
{0, uint256S("0x00000001f757bb737f6596503e17cd17b0658ce630cc727c0cca81aec47c9f06")},
|
|
451
449
|
}
|
|
452
450
|
};
|
|
453
451
|
|
|
@@ -474,17 +472,11 @@ public:
|
|
|
474
472
|
base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF};
|
|
475
473
|
base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94};
|
|
476
474
|
|
|
477
|
-
bech32_hrp = "
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
*/
|
|
483
|
-
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int min_activation_height)
|
|
484
|
-
{
|
|
485
|
-
consensus.vDeployments[d].nStartTime = nStartTime;
|
|
486
|
-
consensus.vDeployments[d].nTimeout = nTimeout;
|
|
487
|
-
consensus.vDeployments[d].min_activation_height = min_activation_height;
|
|
475
|
+
bech32_hrp = "pcrt";
|
|
476
|
+
fMiningRequiresPeers = false;
|
|
477
|
+
fDefaultConsistencyChecks = true;
|
|
478
|
+
fRequireStandard = false;
|
|
479
|
+
//fMineBlocksOnDemand = true;
|
|
488
480
|
}
|
|
489
481
|
void UpdateActivationParametersFromArgs(const ArgsManager& args);
|
|
490
482
|
};
|
|
@@ -507,9 +499,9 @@ static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& conse
|
|
|
507
499
|
} else if (name == "bip34") {
|
|
508
500
|
consensus.BIP34Height = int{height};
|
|
509
501
|
} else if (name == "dersig") {
|
|
510
|
-
consensus.BIP66Height = int{height};
|
|
502
|
+
//consensus.BIP66Height = int{height};
|
|
511
503
|
} else if (name == "cltv") {
|
|
512
|
-
consensus.BIP65Height = int{height};
|
|
504
|
+
//consensus.BIP65Height = int{height};
|
|
513
505
|
} else if (name == "csv") {
|
|
514
506
|
consensus.CSVHeight = int{height};
|
|
515
507
|
} else {
|
|
@@ -521,40 +513,7 @@ static void MaybeUpdateHeights(const ArgsManager& args, Consensus::Params& conse
|
|
|
521
513
|
void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
|
|
522
514
|
{
|
|
523
515
|
MaybeUpdateHeights(args, consensus);
|
|
524
|
-
|
|
525
|
-
if (!args.IsArgSet("-vbparams")) return;
|
|
526
|
-
|
|
527
|
-
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
|
|
528
|
-
std::vector<std::string> vDeploymentParams;
|
|
529
|
-
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
|
|
530
|
-
if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) {
|
|
531
|
-
throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]");
|
|
532
|
-
}
|
|
533
|
-
int64_t nStartTime, nTimeout;
|
|
534
|
-
int min_activation_height = 0;
|
|
535
|
-
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
|
|
536
|
-
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
|
|
537
|
-
}
|
|
538
|
-
if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
|
|
539
|
-
throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
|
|
540
|
-
}
|
|
541
|
-
if (vDeploymentParams.size() >= 4 && !ParseInt32(vDeploymentParams[3], &min_activation_height)) {
|
|
542
|
-
throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3]));
|
|
543
|
-
}
|
|
544
|
-
bool found = false;
|
|
545
|
-
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
|
|
546
|
-
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
|
|
547
|
-
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, min_activation_height);
|
|
548
|
-
found = true;
|
|
549
|
-
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d\n", vDeploymentParams[0], nStartTime, nTimeout, min_activation_height);
|
|
550
|
-
break;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
if (!found) {
|
|
554
|
-
throw std::runtime_error(strprintf("Invalid deployment (%s)", vDeploymentParams[0]));
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
516
|
+
}
|
|
558
517
|
|
|
559
518
|
static std::unique_ptr<const CChainParams> globalChainParams;
|
|
560
519
|
|
|
@@ -93,6 +93,8 @@ public:
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
const CBlock& GenesisBlock() const { return genesis; }
|
|
96
|
+
/** Make miner wait to have peers to avoid wasting work */
|
|
97
|
+
bool MiningRequiresPeers() const { return fMiningRequiresPeers; }
|
|
96
98
|
/** Default value for -checkmempool and -checkblockindex argument */
|
|
97
99
|
bool DefaultConsistencyChecks() const { return fDefaultConsistencyChecks; }
|
|
98
100
|
/** Policy: Filter transactions that do not match well-defined patterns */
|
|
@@ -104,8 +106,6 @@ public:
|
|
|
104
106
|
uint64_t PruneAfterHeight() const { return nPruneAfterHeight; }
|
|
105
107
|
/** Minimum free space (in GB) needed for data directory */
|
|
106
108
|
uint64_t AssumedBlockchainSize() const { return m_assumed_blockchain_size; }
|
|
107
|
-
/** Minimum free space (in GB) needed for data directory when pruned; Does not include prune target*/
|
|
108
|
-
uint64_t AssumedChainStateSize() const { return m_assumed_chain_state_size; }
|
|
109
109
|
/** Whether it is possible to mine blocks on demand (no retargeting) */
|
|
110
110
|
bool MineBlocksOnDemand() const { return consensus.fPowNoRetargeting; }
|
|
111
111
|
/** Return the network string */
|
|
@@ -130,13 +130,13 @@ protected:
|
|
|
130
130
|
uint16_t nDefaultPort;
|
|
131
131
|
uint64_t nPruneAfterHeight;
|
|
132
132
|
uint64_t m_assumed_blockchain_size;
|
|
133
|
-
uint64_t m_assumed_chain_state_size;
|
|
134
133
|
std::vector<std::string> vSeeds;
|
|
135
134
|
std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES];
|
|
136
135
|
std::string bech32_hrp;
|
|
137
136
|
std::string strNetworkID;
|
|
138
137
|
CBlock genesis;
|
|
139
138
|
std::vector<uint8_t> vFixedSeeds;
|
|
139
|
+
bool fMiningRequiresPeers;
|
|
140
140
|
bool fDefaultConsistencyChecks;
|
|
141
141
|
bool fRequireStandard;
|
|
142
142
|
bool m_is_test_chain;
|
|
@@ -43,13 +43,13 @@ const CBaseChainParams& BaseParams()
|
|
|
43
43
|
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
|
|
44
44
|
{
|
|
45
45
|
if (chain == CBaseChainParams::MAIN) {
|
|
46
|
-
return std::make_unique<CBaseChainParams>("",
|
|
46
|
+
return std::make_unique<CBaseChainParams>("", 9902, 9903);
|
|
47
47
|
} else if (chain == CBaseChainParams::TESTNET) {
|
|
48
|
-
return std::make_unique<CBaseChainParams>("testnet3",
|
|
48
|
+
return std::make_unique<CBaseChainParams>("testnet3", 9904, 9905);
|
|
49
49
|
} else if (chain == CBaseChainParams::SIGNET) {
|
|
50
50
|
return std::make_unique<CBaseChainParams>("signet", 38332, 38334);
|
|
51
51
|
} else if (chain == CBaseChainParams::REGTEST) {
|
|
52
|
-
return std::make_unique<CBaseChainParams>("regtest", 18443,
|
|
52
|
+
return std::make_unique<CBaseChainParams>("regtest", 18443, 18444);
|
|
53
53
|
}
|
|
54
54
|
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
|
|
55
55
|
}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
const std::string CLIENT_NAME("Satoshi");
|
|
20
20
|
|
|
21
|
+
#define CLIENT_VERSION_SUFFIX " Coccinellidae"
|
|
21
22
|
|
|
22
23
|
#ifdef HAVE_BUILD_INFO
|
|
23
24
|
#include <obj/build.h>
|
|
@@ -34,7 +35,8 @@ const std::string CLIENT_NAME("Satoshi");
|
|
|
34
35
|
#define BUILD_DESC BUILD_GIT_TAG
|
|
35
36
|
#define BUILD_SUFFIX ""
|
|
36
37
|
#else
|
|
37
|
-
#define BUILD_DESC "v"
|
|
38
|
+
#define BUILD_DESC "v" STRINGIZE(PEERCOIN_VERSION_MAJOR) "." STRINGIZE(PEERCOIN_VERSION_MINOR) \
|
|
39
|
+
"." STRINGIZE(PEERCOIN_VERSION_REVISION) "." STRINGIZE(PEERCOIN_VERSION_BUILD)
|
|
38
40
|
#if CLIENT_VERSION_IS_RELEASE
|
|
39
41
|
#define BUILD_SUFFIX ""
|
|
40
42
|
#elif defined(BUILD_GIT_COMMIT)
|
|
@@ -53,7 +55,7 @@ static std::string FormatVersion(int nVersion)
|
|
|
53
55
|
|
|
54
56
|
std::string FormatFullVersion()
|
|
55
57
|
{
|
|
56
|
-
static const std::string CLIENT_BUILD(BUILD_DESC
|
|
58
|
+
static const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX);
|
|
57
59
|
return CLIENT_BUILD;
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -74,6 +76,8 @@ std::string FormatSubVersion(const std::string& name, int nClientVersion, const
|
|
|
74
76
|
ss << ")";
|
|
75
77
|
}
|
|
76
78
|
ss << "/";
|
|
79
|
+
ss << "Peercoin:" << FormatVersion(PEERCOIN_VERSION);
|
|
80
|
+
ss << "(" << FormatFullVersion() << ")/";
|
|
77
81
|
return ss.str();
|
|
78
82
|
}
|
|
79
83
|
|
|
@@ -91,7 +95,7 @@ std::string CopyrightHolders(const std::string& strPrefix)
|
|
|
91
95
|
|
|
92
96
|
std::string LicenseInfo()
|
|
93
97
|
{
|
|
94
|
-
const std::string URL_SOURCE_CODE = "<https://github.com/
|
|
98
|
+
const std::string URL_SOURCE_CODE = "<https://github.com/peercoin/peercoin>";
|
|
95
99
|
|
|
96
100
|
return CopyrightHolders(strprintf(_("Copyright (C) %i-%i").translated, 2009, COPYRIGHT_YEAR) + " ") + "\n" +
|
|
97
101
|
"\n" +
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
#endif //HAVE_CONFIG_H
|
|
13
13
|
|
|
14
14
|
// Check that required client information is defined
|
|
15
|
-
#if !defined(
|
|
15
|
+
#if !defined(PEERCOIN_VERSION_MAJOR) || !defined(PEERCOIN_VERSION_MINOR) || !defined(PEERCOIN_VERSION_REVISION) || !defined(PEERCOIN_VERSION_BUILD)
|
|
16
16
|
#error Client version information missing: version is not defined by bitcoin-config.h or in any other way
|
|
17
17
|
#endif
|
|
18
18
|
|
|
@@ -35,6 +35,13 @@ static const int CLIENT_VERSION =
|
|
|
35
35
|
+ 100 * CLIENT_VERSION_MINOR
|
|
36
36
|
+ 1 * CLIENT_VERSION_BUILD;
|
|
37
37
|
|
|
38
|
+
// note: peercoin version is used for display purpose AND to accept alerts
|
|
39
|
+
static const int PEERCOIN_VERSION =
|
|
40
|
+
1000000 * PEERCOIN_VERSION_MAJOR
|
|
41
|
+
+ 10000 * PEERCOIN_VERSION_MINOR
|
|
42
|
+
+ 100 * PEERCOIN_VERSION_REVISION
|
|
43
|
+
+ 1 * PEERCOIN_VERSION_BUILD;
|
|
44
|
+
|
|
38
45
|
extern const std::string CLIENT_NAME;
|
|
39
46
|
|
|
40
47
|
|
|
@@ -64,9 +64,10 @@ bool CCoinsViewCache::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
|
|
64
64
|
return false;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite) {
|
|
67
|
+
void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possible_overwrite, bool skipZeroValue) {
|
|
68
68
|
assert(!coin.IsSpent());
|
|
69
69
|
if (coin.out.scriptPubKey.IsUnspendable()) return;
|
|
70
|
+
if (coin.out.nValue == 0 && skipZeroValue) return;
|
|
70
71
|
CCoinsMap::iterator it;
|
|
71
72
|
bool inserted;
|
|
72
73
|
std::tie(it, inserted) = cacheCoins.emplace(std::piecewise_construct, std::forward_as_tuple(outpoint), std::tuple<>());
|
|
@@ -112,14 +113,14 @@ void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coi
|
|
|
112
113
|
std::forward_as_tuple(std::move(coin), CCoinsCacheEntry::DIRTY));
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
|
|
116
|
+
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite, bool skipZeroValue) {
|
|
116
117
|
bool fCoinbase = tx.IsCoinBase();
|
|
117
118
|
const uint256& txid = tx.GetHash();
|
|
118
119
|
for (size_t i = 0; i < tx.vout.size(); ++i) {
|
|
119
120
|
bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;
|
|
120
121
|
// Coinbase transactions can always be overwritten, in order to correctly
|
|
121
122
|
// deal with the pre-BIP30 occurrences of duplicate coinbase transactions.
|
|
122
|
-
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite);
|
|
123
|
+
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase, tx.IsCoinStake(), tx.nTime), overwrite, skipZeroValue);
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -39,29 +39,48 @@ public:
|
|
|
39
39
|
//! at which height this containing transaction was included in the active block chain
|
|
40
40
|
uint32_t nHeight : 31;
|
|
41
41
|
|
|
42
|
+
// peercoin: whether transaction is a coinstake
|
|
43
|
+
bool fCoinStake;
|
|
44
|
+
|
|
45
|
+
// peercoin: transaction timestamp
|
|
46
|
+
unsigned int nTime;
|
|
47
|
+
|
|
42
48
|
//! construct a Coin from a CTxOut and height/coinbase information.
|
|
43
|
-
Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn
|
|
44
|
-
|
|
49
|
+
Coin(CTxOut&& outIn, int nHeightIn, bool fCoinBaseIn, bool fCoinStakeIn, int nTimeIn) :
|
|
50
|
+
out(std::move(outIn)), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), fCoinStake(fCoinStakeIn), nTime(nTimeIn) {}
|
|
51
|
+
Coin(const CTxOut& outIn, int nHeightIn, bool fCoinBaseIn, bool fCoinStakeIn, int nTimeIn) :
|
|
52
|
+
out(outIn), fCoinBase(fCoinBaseIn), nHeight(nHeightIn), fCoinStake(fCoinStakeIn), nTime(nTimeIn) {}
|
|
45
53
|
|
|
46
54
|
void Clear() {
|
|
47
55
|
out.SetNull();
|
|
48
56
|
fCoinBase = false;
|
|
49
57
|
nHeight = 0;
|
|
58
|
+
fCoinStake = false;
|
|
59
|
+
nTime = 0;
|
|
50
60
|
}
|
|
51
61
|
|
|
52
62
|
//! empty constructor
|
|
53
|
-
Coin() : fCoinBase(false), nHeight(0) { }
|
|
63
|
+
Coin() : fCoinBase(false), nHeight(0), fCoinStake(false), nTime(0) { }
|
|
54
64
|
|
|
55
65
|
bool IsCoinBase() const {
|
|
56
66
|
return fCoinBase;
|
|
57
67
|
}
|
|
58
68
|
|
|
69
|
+
bool IsCoinStake() const { // peercoin: coinstake
|
|
70
|
+
return fCoinStake;
|
|
71
|
+
}
|
|
72
|
+
|
|
59
73
|
template<typename Stream>
|
|
60
74
|
void Serialize(Stream &s) const {
|
|
61
75
|
assert(!IsSpent());
|
|
62
76
|
uint32_t code = nHeight * uint32_t{2} + fCoinBase;
|
|
63
77
|
::Serialize(s, VARINT(code));
|
|
64
78
|
::Serialize(s, Using<TxOutCompression>(out));
|
|
79
|
+
// peercoin flags
|
|
80
|
+
unsigned int nFlag = fCoinStake? 1 : 0;
|
|
81
|
+
::Serialize(s, VARINT(nFlag));
|
|
82
|
+
// peercoin transaction timestamp
|
|
83
|
+
::Serialize(s, VARINT(nTime));
|
|
65
84
|
}
|
|
66
85
|
|
|
67
86
|
template<typename Stream>
|
|
@@ -71,6 +90,12 @@ public:
|
|
|
71
90
|
nHeight = code >> 1;
|
|
72
91
|
fCoinBase = code & 1;
|
|
73
92
|
::Unserialize(s, Using<TxOutCompression>(out));
|
|
93
|
+
// peercoin flags
|
|
94
|
+
unsigned int nFlag = 0;
|
|
95
|
+
::Unserialize(s, VARINT(nFlag));
|
|
96
|
+
fCoinStake = nFlag & 1;
|
|
97
|
+
// peercoin transaction timestamp
|
|
98
|
+
::Unserialize(s, VARINT(nTime));
|
|
74
99
|
}
|
|
75
100
|
|
|
76
101
|
/** Either this coin never existed (see e.g. coinEmpty in coins.cpp), or it
|
|
@@ -264,7 +289,7 @@ public:
|
|
|
264
289
|
* Add a coin. Set possible_overwrite to true if an unspent version may
|
|
265
290
|
* already exist in the cache.
|
|
266
291
|
*/
|
|
267
|
-
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite);
|
|
292
|
+
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite, bool skipZeroValue = false);
|
|
268
293
|
|
|
269
294
|
/**
|
|
270
295
|
* Emplace a coin into cacheCoins without performing any checks, marking
|
|
@@ -325,7 +350,7 @@ private:
|
|
|
325
350
|
//! an overwrite.
|
|
326
351
|
// TODO: pass in a boolean to limit these possible overwrites to known
|
|
327
352
|
// (pre-BIP34) cases.
|
|
328
|
-
void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool check = false);
|
|
353
|
+
void AddCoins(CCoinsViewCache& cache, const CTransaction& tx, int nHeight, bool check = false, bool skipZeroValue = false);
|
|
329
354
|
|
|
330
355
|
//! Utility function to find any unspent output with a given txid.
|
|
331
356
|
//! This function can be quite expensive because in the event of a transaction
|
|
@@ -7,12 +7,22 @@
|
|
|
7
7
|
#define BITCOIN_CONSENSUS_AMOUNT_H
|
|
8
8
|
|
|
9
9
|
#include <cstdint>
|
|
10
|
+
#include <string>
|
|
10
11
|
|
|
11
12
|
/** Amount in satoshis (Can be negative) */
|
|
12
13
|
typedef int64_t CAmount;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
static constexpr CAmount
|
|
15
|
+
static constexpr CAmount COIN = 1000000;
|
|
16
|
+
static constexpr CAmount CENT = 10000;
|
|
17
|
+
|
|
18
|
+
static const CAmount MIN_TX_FEE_PREV7 = CENT;
|
|
19
|
+
static const CAmount MIN_TX_FEE = CENT / 10;
|
|
20
|
+
static const CAmount PERKB_TX_FEE = CENT;
|
|
21
|
+
static const CAmount MIN_TXOUT_AMOUNT = CENT;
|
|
22
|
+
static const CAmount MAX_MINT_PROOF_OF_WORK = 9999 * COIN;
|
|
23
|
+
static const CAmount MAX_MINT_PROOF_OF_WORK_V10 = 50 * COIN;
|
|
24
|
+
static const std::string CURRENCY_UNIT = "PPC";
|
|
25
|
+
static const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit
|
|
16
26
|
|
|
17
27
|
/** No amount larger than this (in satoshi) is valid.
|
|
18
28
|
*
|
|
@@ -33,52 +33,16 @@ enum DeploymentPos : uint16_t {
|
|
|
33
33
|
};
|
|
34
34
|
constexpr bool ValidDeployment(DeploymentPos dep) { return dep < MAX_VERSION_BITS_DEPLOYMENTS; }
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* Struct for each individual consensus rule change using BIP9.
|
|
38
|
-
*/
|
|
39
|
-
struct BIP9Deployment {
|
|
40
|
-
/** Bit position to select the particular bit in nVersion. */
|
|
41
|
-
int bit;
|
|
42
|
-
/** Start MedianTime for version bits miner confirmation. Can be a date in the past */
|
|
43
|
-
int64_t nStartTime;
|
|
44
|
-
/** Timeout/expiry MedianTime for the deployment attempt. */
|
|
45
|
-
int64_t nTimeout;
|
|
46
|
-
/** If lock in occurs, delay activation until at least this block
|
|
47
|
-
* height. Note that activation will only occur on a retarget
|
|
48
|
-
* boundary.
|
|
49
|
-
*/
|
|
50
|
-
int min_activation_height{0};
|
|
51
|
-
|
|
52
|
-
/** Constant for nTimeout very far in the future. */
|
|
53
|
-
static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
|
|
54
|
-
|
|
55
|
-
/** Special value for nStartTime indicating that the deployment is always active.
|
|
56
|
-
* This is useful for testing, as it means tests don't need to deal with the activation
|
|
57
|
-
* process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
|
|
58
|
-
* behaviour during activation cannot use this. */
|
|
59
|
-
static constexpr int64_t ALWAYS_ACTIVE = -1;
|
|
60
|
-
|
|
61
|
-
/** Special value for nStartTime indicating that the deployment is never active.
|
|
62
|
-
* This is useful for integrating the code changes for a new feature
|
|
63
|
-
* prior to deploying it on some or all networks. */
|
|
64
|
-
static constexpr int64_t NEVER_ACTIVE = -2;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
36
|
/**
|
|
68
37
|
* Parameters that influence chain consensus.
|
|
69
38
|
*/
|
|
70
39
|
struct Params {
|
|
71
40
|
uint256 hashGenesisBlock;
|
|
72
|
-
int nSubsidyHalvingInterval;
|
|
73
41
|
/* Block hash that is excepted from BIP16 enforcement */
|
|
74
42
|
uint256 BIP16Exception;
|
|
75
43
|
/** Block height and hash at which BIP34 becomes active */
|
|
76
44
|
int BIP34Height;
|
|
77
45
|
uint256 BIP34Hash;
|
|
78
|
-
/** Block height at which BIP65 becomes active */
|
|
79
|
-
int BIP65Height;
|
|
80
|
-
/** Block height at which BIP66 becomes active */
|
|
81
|
-
int BIP66Height;
|
|
82
46
|
/** Block height at which CSV (BIP68, BIP112 and BIP113) becomes active */
|
|
83
47
|
int CSVHeight;
|
|
84
48
|
/** Block height at which Segwit (BIP141, BIP143 and BIP147) becomes active.
|
|
@@ -90,19 +54,16 @@ struct Params {
|
|
|
90
54
|
int MinBIP9WarningHeight;
|
|
91
55
|
/**
|
|
92
56
|
* Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,
|
|
93
|
-
* (nPowTargetTimespan / nPowTargetSpacing)
|
|
57
|
+
* (nPowTargetTimespan / nPowTargetSpacing)
|
|
94
58
|
* Examples: 1916 for 95%, 1512 for testchains.
|
|
95
59
|
*/
|
|
96
60
|
uint32_t nRuleChangeActivationThreshold;
|
|
97
61
|
uint32_t nMinerConfirmationWindow;
|
|
98
|
-
BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS];
|
|
99
62
|
/** Proof of work parameters */
|
|
100
63
|
uint256 powLimit;
|
|
101
64
|
bool fPowAllowMinDifficultyBlocks;
|
|
102
65
|
bool fPowNoRetargeting;
|
|
103
66
|
int64_t nPowTargetSpacing;
|
|
104
|
-
int64_t nPowTargetTimespan;
|
|
105
|
-
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
|
|
106
67
|
/** The best chain should have at least this much work */
|
|
107
68
|
uint256 nMinimumChainWork;
|
|
108
69
|
/** By default assume that the signatures in ancestors of this block are valid */
|
|
@@ -114,23 +75,15 @@ struct Params {
|
|
|
114
75
|
*/
|
|
115
76
|
bool signet_blocks{false};
|
|
116
77
|
std::vector<uint8_t> signet_challenge;
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return BIP66Height;
|
|
127
|
-
case DEPLOYMENT_CSV:
|
|
128
|
-
return CSVHeight;
|
|
129
|
-
case DEPLOYMENT_SEGWIT:
|
|
130
|
-
return SegwitHeight;
|
|
131
|
-
} // no default case, so the compiler can warn about missing cases
|
|
132
|
-
return std::numeric_limits<int>::max();
|
|
133
|
-
}
|
|
78
|
+
/** peercoin stuff */
|
|
79
|
+
uint256 bnInitialHashTarget;
|
|
80
|
+
int64_t nStakeTargetSpacing;
|
|
81
|
+
int64_t nTargetSpacingWorkMax;
|
|
82
|
+
int64_t nTargetTimespan;
|
|
83
|
+
int64_t nStakeMinAge;
|
|
84
|
+
int64_t nStakeMaxAge;
|
|
85
|
+
int64_t nModifierInterval;
|
|
86
|
+
int nCoinbaseMaturity; // Coinbase transaction outputs can only be spent after this number of new blocks (network rule)
|
|
134
87
|
};
|
|
135
88
|
|
|
136
89
|
} // namespace Consensus
|
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
#include <consensus/amount.h>
|
|
8
8
|
#include <primitives/transaction.h>
|
|
9
9
|
#include <consensus/validation.h>
|
|
10
|
+
#include <chainparams.h>
|
|
11
|
+
|
|
12
|
+
bool IsZeroAllowed(const unsigned int nTimeTx)
|
|
13
|
+
{
|
|
14
|
+
return (nTimeTx >= 1447700000 ); // very crude approximation to prevent linking with kernel.cpp
|
|
15
|
+
}
|
|
10
16
|
|
|
11
17
|
bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
|
|
12
18
|
{
|
|
@@ -30,6 +36,10 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
|
|
|
30
36
|
nValueOut += txout.nValue;
|
|
31
37
|
if (!MoneyRange(nValueOut))
|
|
32
38
|
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txouttotal-toolarge");
|
|
39
|
+
// peercoin: enforce minimum output amount
|
|
40
|
+
if ((!txout.IsEmpty()) && txout.nValue < MIN_TXOUT_AMOUNT &&
|
|
41
|
+
(tx.nVersion < 3 && !(IsZeroAllowed(tx.nTime) && (txout.nValue == 0))))
|
|
42
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-txoutvalue-belowminimum");
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
// Check for duplicate inputs (see CVE-2018-17144)
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
#include <consensus/validation.h>
|
|
12
12
|
#include <primitives/transaction.h>
|
|
13
13
|
#include <script/interpreter.h>
|
|
14
|
+
#include <kernel.h>
|
|
15
|
+
#include <validation.h> // GetCoinAge()
|
|
16
|
+
|
|
14
17
|
#include <util/moneystr.h>
|
|
15
18
|
|
|
16
19
|
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
|
|
@@ -164,7 +167,7 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i
|
|
|
164
167
|
return nSigOps;
|
|
165
168
|
}
|
|
166
169
|
|
|
167
|
-
bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee)
|
|
170
|
+
bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, const Consensus::Params& params, unsigned int nTimeTx, uint64_t nMoneySupply)
|
|
168
171
|
{
|
|
169
172
|
// are the actual inputs available?
|
|
170
173
|
if (!inputs.HaveInputs(tx)) {
|
|
@@ -179,11 +182,15 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
|
|
|
179
182
|
assert(!coin.IsSpent());
|
|
180
183
|
|
|
181
184
|
// If prev is coinbase, check that it's matured
|
|
182
|
-
if (coin.IsCoinBase() && nSpendHeight - coin.nHeight <
|
|
183
|
-
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase",
|
|
185
|
+
if ((coin.IsCoinBase() || coin.IsCoinStake()) && nSpendHeight - coin.nHeight < params.nCoinbaseMaturity) {
|
|
186
|
+
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-premature-spend-of-coinbase/coinstake",
|
|
184
187
|
strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight));
|
|
185
188
|
}
|
|
186
189
|
|
|
190
|
+
// peercoin: check transaction timestamp
|
|
191
|
+
if (coin.nTime > nTimeTx)
|
|
192
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spent-too-early", strprintf("%s : transaction timestamp earlier than input transaction", __func__));
|
|
193
|
+
|
|
187
194
|
// Check for negative or overflow input values
|
|
188
195
|
nValueIn += coin.out.nValue;
|
|
189
196
|
if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) {
|
|
@@ -191,18 +198,52 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state,
|
|
|
191
198
|
}
|
|
192
199
|
}
|
|
193
200
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
if (tx.IsCoinStake())
|
|
202
|
+
{
|
|
203
|
+
// peercoin: coin stake tx earns reward instead of paying fee
|
|
204
|
+
uint64_t nCoinAge;
|
|
205
|
+
if (!GetCoinAge(tx, inputs, nCoinAge, nTimeTx))
|
|
206
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "unable to get coin age for coinstake");
|
|
207
|
+
CAmount nStakeReward = tx.GetValueOut() - nValueIn;
|
|
208
|
+
CAmount nCoinstakeCost = (GetMinFee(tx, nTimeTx) < PERKB_TX_FEE) ? 0 : (GetMinFee(tx, nTimeTx) - PERKB_TX_FEE);
|
|
209
|
+
if (nMoneySupply && nStakeReward > GetProofOfStakeReward(nCoinAge, nTimeTx, nMoneySupply) - nCoinstakeCost)
|
|
210
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-coinstake-too-large");
|
|
198
211
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
212
|
+
else
|
|
213
|
+
{
|
|
214
|
+
const CAmount value_out = tx.GetValueOut();
|
|
215
|
+
if (nValueIn < value_out) {
|
|
216
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-in-belowout",
|
|
217
|
+
strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out)));
|
|
218
|
+
}
|
|
219
|
+
// Tally transaction fees
|
|
220
|
+
const CAmount txfee_aux = nValueIn - value_out;
|
|
221
|
+
if (!MoneyRange(txfee_aux)) {
|
|
222
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-outofrange");
|
|
223
|
+
}
|
|
224
|
+
// peercoin: enforce transaction fees for every block
|
|
225
|
+
if (txfee_aux < GetMinFee(tx, nTimeTx))
|
|
226
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-fee-not-enough");
|
|
227
|
+
txfee = txfee_aux;
|
|
204
228
|
}
|
|
205
|
-
|
|
206
|
-
txfee = txfee_aux;
|
|
207
229
|
return true;
|
|
208
230
|
}
|
|
231
|
+
|
|
232
|
+
CAmount GetMinFee(const CTransaction& tx, unsigned int nTimeTx)
|
|
233
|
+
{
|
|
234
|
+
size_t nBytes = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
|
235
|
+
return GetMinFee(nBytes, nTimeTx);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
CAmount GetMinFee(size_t nBytes, uint32_t nTime)
|
|
239
|
+
{
|
|
240
|
+
CAmount nMinFee;
|
|
241
|
+
if (IsProtocolV07(nTime) || !nTime) // RFC-0007
|
|
242
|
+
nMinFee = (nBytes < 100) ? MIN_TX_FEE : (CAmount)(nBytes * (PERKB_TX_FEE / 1000));
|
|
243
|
+
else
|
|
244
|
+
nMinFee = (1 + (CAmount)nBytes / 1000) * PERKB_TX_FEE;
|
|
245
|
+
|
|
246
|
+
if (!MoneyRange(nMinFee))
|
|
247
|
+
nMinFee = MAX_MONEY;
|
|
248
|
+
return nMinFee;
|
|
249
|
+
}
|
|
@@ -18,13 +18,14 @@ class TxValidationState;
|
|
|
18
18
|
/** Transaction validation functions */
|
|
19
19
|
|
|
20
20
|
namespace Consensus {
|
|
21
|
+
struct Params;
|
|
21
22
|
/**
|
|
22
23
|
* Check whether all inputs of this transaction are valid (no double spends and amounts)
|
|
23
24
|
* This does not modify the UTXO set. This does not check scripts and sigs.
|
|
24
25
|
* @param[out] txfee Set to the transaction fee if successful.
|
|
25
26
|
* Preconditions: tx.IsCoinBase() is false.
|
|
26
27
|
*/
|
|
27
|
-
|
|
28
|
+
bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, const Consensus::Params& params, unsigned int nTimeTx, uint64_t nMoneySupply=0);
|
|
28
29
|
} // namespace Consensus
|
|
29
30
|
|
|
30
31
|
/** Auxiliary functions for transaction validation (ideally should not be exposed) */
|
|
@@ -75,4 +76,8 @@ bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> loc
|
|
|
75
76
|
*/
|
|
76
77
|
bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>& prevHeights, const CBlockIndex& block);
|
|
77
78
|
|
|
79
|
+
// peercoin: minimum fee for transaction to be accepted in a blockchain.
|
|
80
|
+
CAmount GetMinFee(const CTransaction& tx, unsigned int nTimeTx);
|
|
81
|
+
CAmount GetMinFee(size_t nBytes, uint32_t nTime);
|
|
82
|
+
|
|
78
83
|
#endif // BITCOIN_CONSENSUS_TX_VERIFY_H
|
|
@@ -29,7 +29,7 @@ UniValue ValueFromAmount(const CAmount amount)
|
|
|
29
29
|
remainder = -remainder;
|
|
30
30
|
}
|
|
31
31
|
return UniValue(UniValue::VNUM,
|
|
32
|
-
strprintf("%s%d.%
|
|
32
|
+
strprintf("%s%d.%06d", amount < 0 ? "-" : "", quotient, remainder));
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
std::string FormatScript(const CScript& script)
|
|
@@ -174,6 +174,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
|
|
|
174
174
|
// Transaction version is actually unsigned in consensus checks, just signed in memory,
|
|
175
175
|
// so cast to unsigned before giving it to the user.
|
|
176
176
|
entry.pushKV("version", static_cast<int64_t>(static_cast<uint32_t>(tx.nVersion)));
|
|
177
|
+
entry.pushKV("time", (int64_t)tx.nTime);
|
|
177
178
|
entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION));
|
|
178
179
|
entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR);
|
|
179
180
|
entry.pushKV("weight", GetTransactionWeight(tx));
|
|
@@ -252,8 +253,10 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
|
|
|
252
253
|
|
|
253
254
|
if (have_undo) {
|
|
254
255
|
const CAmount fee = amt_total_in - amt_total_out;
|
|
255
|
-
|
|
256
|
-
|
|
256
|
+
if (fee > 0)
|
|
257
|
+
entry.pushKV("fee", ValueFromAmount(fee));
|
|
258
|
+
else
|
|
259
|
+
entry.pushKV("reward", ValueFromAmount(-fee));
|
|
257
260
|
}
|
|
258
261
|
|
|
259
262
|
if (!hashBlock.IsNull())
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#include <deploymentstatus.h>
|
|
6
|
-
|
|
7
|
-
#include <consensus/params.h>
|
|
8
|
-
#include <versionbits.h>
|
|
9
|
-
|
|
10
|
-
#include <type_traits>
|
|
11
|
-
|
|
12
|
-
VersionBitsCache g_versionbitscache;
|
|
13
|
-
|
|
14
|
-
/* Basic sanity checking for BuriedDeployment/DeploymentPos enums and
|
|
15
|
-
* ValidDeployment check */
|
|
16
|
-
|
|
17
|
-
static_assert(ValidDeployment(Consensus::DEPLOYMENT_TESTDUMMY), "sanity check of DeploymentPos failed (TESTDUMMY not valid)");
|
|
18
|
-
static_assert(!ValidDeployment(Consensus::MAX_VERSION_BITS_DEPLOYMENTS), "sanity check of DeploymentPos failed (MAX value considered valid)");
|
|
19
|
-
static_assert(!ValidDeployment(static_cast<Consensus::BuriedDeployment>(Consensus::DEPLOYMENT_TESTDUMMY)), "sanity check of BuriedDeployment failed (overlaps with DeploymentPos)");
|
|
20
|
-
|
|
21
|
-
/* ValidDeployment only checks upper bounds for ensuring validity.
|
|
22
|
-
* This checks that the lowest possible value or the type is also a
|
|
23
|
-
* (specific) valid deployment so that lower bounds don't need to be checked.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
template<typename T, T x>
|
|
27
|
-
static constexpr bool is_minimum()
|
|
28
|
-
{
|
|
29
|
-
using U = typename std::underlying_type<T>::type;
|
|
30
|
-
return x == std::numeric_limits<U>::min();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
static_assert(is_minimum<Consensus::BuriedDeployment, Consensus::DEPLOYMENT_HEIGHTINCB>(), "heightincb is not minimum value for BuriedDeployment");
|
|
34
|
-
static_assert(is_minimum<Consensus::DeploymentPos, Consensus::DEPLOYMENT_TESTDUMMY>(), "testdummy is not minimum value for DeploymentPos");
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2020-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#ifndef BITCOIN_DEPLOYMENTSTATUS_H
|
|
6
|
-
#define BITCOIN_DEPLOYMENTSTATUS_H
|
|
7
|
-
|
|
8
|
-
#include <chain.h>
|
|
9
|
-
#include <versionbits.h>
|
|
10
|
-
|
|
11
|
-
#include <limits>
|
|
12
|
-
|
|
13
|
-
/** Global cache for versionbits deployment status */
|
|
14
|
-
extern VersionBitsCache g_versionbitscache;
|
|
15
|
-
|
|
16
|
-
/** Determine if a deployment is active for the next block */
|
|
17
|
-
inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::BuriedDeployment dep)
|
|
18
|
-
{
|
|
19
|
-
assert(Consensus::ValidDeployment(dep));
|
|
20
|
-
return (pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1) >= params.DeploymentHeight(dep);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep)
|
|
24
|
-
{
|
|
25
|
-
assert(Consensus::ValidDeployment(dep));
|
|
26
|
-
return ThresholdState::ACTIVE == g_versionbitscache.State(pindexPrev, params, dep);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Determine if a deployment is active for this block */
|
|
30
|
-
inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::BuriedDeployment dep)
|
|
31
|
-
{
|
|
32
|
-
assert(Consensus::ValidDeployment(dep));
|
|
33
|
-
return index.nHeight >= params.DeploymentHeight(dep);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
inline bool DeploymentActiveAt(const CBlockIndex& index, const Consensus::Params& params, Consensus::DeploymentPos dep)
|
|
37
|
-
{
|
|
38
|
-
assert(Consensus::ValidDeployment(dep));
|
|
39
|
-
return DeploymentActiveAfter(index.pprev, params, dep);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Determine if a deployment is enabled (can ever be active) */
|
|
43
|
-
inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::BuriedDeployment dep)
|
|
44
|
-
{
|
|
45
|
-
assert(Consensus::ValidDeployment(dep));
|
|
46
|
-
return params.DeploymentHeight(dep) != std::numeric_limits<int>::max();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::DeploymentPos dep)
|
|
50
|
-
{
|
|
51
|
-
assert(Consensus::ValidDeployment(dep));
|
|
52
|
-
return params.vDeployments[dep].nStartTime != Consensus::BIP9Deployment::NEVER_ACTIVE;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
#endif // BITCOIN_DEPLOYMENTSTATUS_H
|
|
@@ -35,7 +35,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
|
|
|
35
35
|
"-fallbackfee=<amt>",
|
|
36
36
|
"-keypool=<n>",
|
|
37
37
|
"-maxapsfee=<n>",
|
|
38
|
-
"-maxtxfee=<amt>",
|
|
39
38
|
"-mintxfee=<amt>",
|
|
40
39
|
"-paytxfee=<amt>",
|
|
41
40
|
"-signer=<cmd>",
|
|
@@ -45,7 +44,6 @@ void DummyWalletInit::AddWalletOptions(ArgsManager& argsman) const
|
|
|
45
44
|
"-walletbroadcast",
|
|
46
45
|
"-walletdir=<dir>",
|
|
47
46
|
"-walletnotify=<cmd>",
|
|
48
|
-
"-walletrbf",
|
|
49
47
|
"-dblogsize=<n>",
|
|
50
48
|
"-flushwallet",
|
|
51
49
|
"-privdb",
|
|
@@ -94,3 +94,12 @@ CHashWriter TaggedHash(const std::string& tag)
|
|
|
94
94
|
writer << taghash << taghash;
|
|
95
95
|
return writer;
|
|
96
96
|
}
|
|
97
|
+
|
|
98
|
+
int32_t peercoinRandseed;
|
|
99
|
+
int univHash(const uint256 &x) {
|
|
100
|
+
int h = peercoinRandseed >> 20;
|
|
101
|
+
const uint32_t *p = x.GetDataPtr();
|
|
102
|
+
for(int i = 0; i < 8; i++)
|
|
103
|
+
h ^= (p[i] >> (h & 0xf)) + (peercoinRandseed >> i);
|
|
104
|
+
return (h + (h >> 16)) & 1023; // 2^n - 1
|
|
105
|
+
}
|
|
@@ -212,4 +212,7 @@ void BIP32Hash(const ChainCode &chainCode, unsigned int nChild, unsigned char he
|
|
|
212
212
|
*/
|
|
213
213
|
CHashWriter TaggedHash(const std::string& tag);
|
|
214
214
|
|
|
215
|
+
extern int32_t peercoinRandseed;
|
|
216
|
+
int univHash(const uint256 &x);
|
|
217
|
+
|
|
215
218
|
#endif // BITCOIN_HASH_H
|
|
@@ -105,7 +105,7 @@ bool BaseIndex::Init()
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
if (prune_violation) {
|
|
108
|
-
return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName()));
|
|
108
|
+
return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName())); // peercoin: should never happen
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
return true;
|
|
@@ -112,8 +112,8 @@ CoinStatsIndex::CoinStatsIndex(size_t n_cache_size, bool f_memory, bool f_wipe)
|
|
|
112
112
|
bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
113
113
|
{
|
|
114
114
|
CBlockUndo block_undo;
|
|
115
|
-
const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
|
|
116
|
-
m_total_subsidy += block_subsidy;
|
|
115
|
+
//const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
|
|
116
|
+
//m_total_subsidy += block_subsidy;
|
|
117
117
|
|
|
118
118
|
// Ignore genesis block
|
|
119
119
|
if (pindex->nHeight > 0) {
|
|
@@ -147,14 +147,14 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
|
147
147
|
|
|
148
148
|
// Skip duplicate txid coinbase transactions (BIP30).
|
|
149
149
|
if (is_bip30_block && tx->IsCoinBase()) {
|
|
150
|
-
m_total_unspendable_amount += block_subsidy;
|
|
151
|
-
m_total_unspendables_bip30 += block_subsidy;
|
|
150
|
+
//m_total_unspendable_amount += block_subsidy;
|
|
151
|
+
//m_total_unspendables_bip30 += block_subsidy;
|
|
152
152
|
continue;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
for (uint32_t j = 0; j < tx->vout.size(); ++j) {
|
|
156
156
|
const CTxOut& out{tx->vout[j]};
|
|
157
|
-
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
|
157
|
+
Coin coin{out, pindex->nHeight, tx->IsCoinBase(), tx->IsCoinStake(), (int)tx->nTime};
|
|
158
158
|
COutPoint outpoint{tx->GetHash(), j};
|
|
159
159
|
|
|
160
160
|
// Skip unspendable coins
|
|
@@ -197,17 +197,17 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
|
197
197
|
}
|
|
198
198
|
} else {
|
|
199
199
|
// genesis block
|
|
200
|
-
m_total_unspendable_amount += block_subsidy;
|
|
201
|
-
m_total_unspendables_genesis_block += block_subsidy;
|
|
200
|
+
//m_total_unspendable_amount += block_subsidy;
|
|
201
|
+
//m_total_unspendables_genesis_block += block_subsidy;
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
// If spent prevouts + block subsidy are still a higher amount than
|
|
205
205
|
// new outputs + coinbase + current unspendable amount this means
|
|
206
206
|
// the miner did not claim the full block reward. Unclaimed block
|
|
207
207
|
// rewards are also unspendable.
|
|
208
|
-
const CAmount unclaimed_rewards{(m_total_prevout_spent_amount + m_total_subsidy) - (m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount + m_total_unspendable_amount)};
|
|
209
|
-
m_total_unspendable_amount += unclaimed_rewards;
|
|
210
|
-
m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
|
|
208
|
+
//const CAmount unclaimed_rewards{(m_total_prevout_spent_amount + m_total_subsidy) - (m_total_new_outputs_ex_coinbase_amount + m_total_coinbase_amount + m_total_unspendable_amount)};
|
|
209
|
+
//m_total_unspendable_amount += unclaimed_rewards;
|
|
210
|
+
//m_total_unspendables_unclaimed_rewards += unclaimed_rewards;
|
|
211
211
|
|
|
212
212
|
std::pair<uint256, DBVal> value;
|
|
213
213
|
value.first = pindex->GetBlockHash();
|
|
@@ -215,14 +215,14 @@ bool CoinStatsIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex)
|
|
|
215
215
|
value.second.bogo_size = m_bogo_size;
|
|
216
216
|
value.second.total_amount = m_total_amount;
|
|
217
217
|
value.second.total_subsidy = m_total_subsidy;
|
|
218
|
-
value.second.total_unspendable_amount = m_total_unspendable_amount;
|
|
218
|
+
//value.second.total_unspendable_amount = m_total_unspendable_amount;
|
|
219
219
|
value.second.total_prevout_spent_amount = m_total_prevout_spent_amount;
|
|
220
220
|
value.second.total_new_outputs_ex_coinbase_amount = m_total_new_outputs_ex_coinbase_amount;
|
|
221
221
|
value.second.total_coinbase_amount = m_total_coinbase_amount;
|
|
222
|
-
value.second.total_unspendables_genesis_block = m_total_unspendables_genesis_block;
|
|
223
|
-
value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
|
|
222
|
+
//value.second.total_unspendables_genesis_block = m_total_unspendables_genesis_block;
|
|
223
|
+
//value.second.total_unspendables_bip30 = m_total_unspendables_bip30;
|
|
224
224
|
value.second.total_unspendables_scripts = m_total_unspendables_scripts;
|
|
225
|
-
value.second.total_unspendables_unclaimed_rewards = m_total_unspendables_unclaimed_rewards;
|
|
225
|
+
//value.second.total_unspendables_unclaimed_rewards = m_total_unspendables_unclaimed_rewards;
|
|
226
226
|
|
|
227
227
|
uint256 out;
|
|
228
228
|
m_muhash.Finalize(out);
|
|
@@ -374,15 +374,15 @@ bool CoinStatsIndex::Init()
|
|
|
374
374
|
m_transaction_output_count = entry.transaction_output_count;
|
|
375
375
|
m_bogo_size = entry.bogo_size;
|
|
376
376
|
m_total_amount = entry.total_amount;
|
|
377
|
-
m_total_subsidy = entry.total_subsidy;
|
|
378
|
-
m_total_unspendable_amount = entry.total_unspendable_amount;
|
|
377
|
+
//m_total_subsidy = entry.total_subsidy;
|
|
378
|
+
//m_total_unspendable_amount = entry.total_unspendable_amount;
|
|
379
379
|
m_total_prevout_spent_amount = entry.total_prevout_spent_amount;
|
|
380
380
|
m_total_new_outputs_ex_coinbase_amount = entry.total_new_outputs_ex_coinbase_amount;
|
|
381
381
|
m_total_coinbase_amount = entry.total_coinbase_amount;
|
|
382
|
-
m_total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
|
|
383
|
-
m_total_unspendables_bip30 = entry.total_unspendables_bip30;
|
|
382
|
+
//m_total_unspendables_genesis_block = entry.total_unspendables_genesis_block;
|
|
383
|
+
//m_total_unspendables_bip30 = entry.total_unspendables_bip30;
|
|
384
384
|
m_total_unspendables_scripts = entry.total_unspendables_scripts;
|
|
385
|
-
m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
|
|
385
|
+
//m_total_unspendables_unclaimed_rewards = entry.total_unspendables_unclaimed_rewards;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
388
|
return true;
|
|
@@ -394,8 +394,8 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|
|
394
394
|
CBlockUndo block_undo;
|
|
395
395
|
std::pair<uint256, DBVal> read_out;
|
|
396
396
|
|
|
397
|
-
const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
|
|
398
|
-
m_total_subsidy -= block_subsidy;
|
|
397
|
+
//const CAmount block_subsidy{GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())};
|
|
398
|
+
//m_total_subsidy -= block_subsidy;
|
|
399
399
|
|
|
400
400
|
// Ignore genesis block
|
|
401
401
|
if (pindex->nHeight > 0) {
|
|
@@ -426,7 +426,7 @@ bool CoinStatsIndex::ReverseBlock(const CBlock& block, const CBlockIndex* pindex
|
|
|
426
426
|
for (uint32_t j = 0; j < tx->vout.size(); ++j) {
|
|
427
427
|
const CTxOut& out{tx->vout[j]};
|
|
428
428
|
COutPoint outpoint{tx->GetHash(), j};
|
|
429
|
-
Coin coin{out, pindex->nHeight, tx->IsCoinBase()};
|
|
429
|
+
Coin coin{out, pindex->nHeight, tx->IsCoinBase(), tx->IsCoinStake(), (int)tx->nTime};
|
|
430
430
|
|
|
431
431
|
// Skip unspendable coins
|
|
432
432
|
if (coin.out.scriptPubKey.IsUnspendable()) {
|
|
@@ -100,3 +100,8 @@ bool TxIndex::FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRe
|
|
|
100
100
|
block_hash = header.GetHash();
|
|
101
101
|
return true;
|
|
102
102
|
}
|
|
103
|
+
|
|
104
|
+
bool TxIndex::FindTxPosition(const uint256& txid, CDiskTxPos& pos) const
|
|
105
|
+
{
|
|
106
|
+
return m_db->ReadTxPos(txid, pos);
|
|
107
|
+
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
#define BITCOIN_INDEX_TXINDEX_H
|
|
7
7
|
|
|
8
8
|
#include <index/base.h>
|
|
9
|
+
#include <index/disktxpos.h>
|
|
10
|
+
#include <primitives/block.h>
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* TxIndex is used to look up transactions included in the blockchain by hash.
|
|
@@ -41,6 +43,9 @@ public:
|
|
|
41
43
|
/// @param[out] tx The transaction itself.
|
|
42
44
|
/// @return true if transaction is found, false otherwise
|
|
43
45
|
bool FindTx(const uint256& tx_hash, uint256& block_hash, CTransactionRef& tx) const;
|
|
46
|
+
|
|
47
|
+
bool FindTxPosition(const uint256& txid, CDiskTxPos& pos) const;
|
|
48
|
+
std::map<uint256,std::pair<CBlockHeader,CTransactionRef>> cachedTxs;
|
|
44
49
|
};
|
|
45
50
|
|
|
46
51
|
/// The global transaction index, used in GetTransaction. May be null.
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
#include <chainparams.h>
|
|
17
17
|
#include <compat/sanity.h>
|
|
18
18
|
#include <consensus/amount.h>
|
|
19
|
-
#include <deploymentstatus.h>
|
|
20
19
|
#include <fs.h>
|
|
21
20
|
#include <hash.h>
|
|
22
21
|
#include <httprpc.h>
|
|
@@ -39,8 +38,6 @@
|
|
|
39
38
|
#include <node/context.h>
|
|
40
39
|
#include <node/miner.h>
|
|
41
40
|
#include <node/ui_interface.h>
|
|
42
|
-
#include <policy/feerate.h>
|
|
43
|
-
#include <policy/fees.h>
|
|
44
41
|
#include <policy/policy.h>
|
|
45
42
|
#include <policy/settings.h>
|
|
46
43
|
#include <protocol.h>
|
|
@@ -62,13 +59,13 @@
|
|
|
62
59
|
#include <util/check.h>
|
|
63
60
|
#include <util/moneystr.h>
|
|
64
61
|
#include <util/strencodings.h>
|
|
65
|
-
#include <util/string.h>
|
|
66
62
|
#include <util/syscall_sandbox.h>
|
|
67
63
|
#include <util/system.h>
|
|
68
64
|
#include <util/thread.h>
|
|
69
65
|
#include <util/threadnames.h>
|
|
70
66
|
#include <util/translation.h>
|
|
71
67
|
#include <validation.h>
|
|
68
|
+
|
|
72
69
|
#include <validationinterface.h>
|
|
73
70
|
#include <walletinitinterface.h>
|
|
74
71
|
|
|
@@ -102,17 +99,14 @@ using node::CacheSizes;
|
|
|
102
99
|
using node::CalculateCacheSizes;
|
|
103
100
|
using node::ChainstateLoadVerifyError;
|
|
104
101
|
using node::ChainstateLoadingError;
|
|
105
|
-
using node::CleanupBlockRevFiles;
|
|
106
102
|
using node::DEFAULT_PRINTPRIORITY;
|
|
107
103
|
using node::DEFAULT_STOPAFTERBLOCKIMPORT;
|
|
108
104
|
using node::LoadChainstate;
|
|
109
105
|
using node::NodeContext;
|
|
110
106
|
using node::ThreadImport;
|
|
111
107
|
using node::VerifyLoadedChainstate;
|
|
112
|
-
using node::fHavePruned;
|
|
113
|
-
using node::fPruneMode;
|
|
114
108
|
using node::fReindex;
|
|
115
|
-
using
|
|
109
|
+
using interfaces::WalletLoader;
|
|
116
110
|
|
|
117
111
|
static const bool DEFAULT_PROXYRANDOMIZE = true;
|
|
118
112
|
static const bool DEFAULT_REST_ENABLE = false;
|
|
@@ -131,7 +125,9 @@ static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";
|
|
|
131
125
|
/**
|
|
132
126
|
* The PID file facilities.
|
|
133
127
|
*/
|
|
134
|
-
static const char* BITCOIN_PID_FILENAME = "
|
|
128
|
+
static const char* BITCOIN_PID_FILENAME = "peercoind.pid";
|
|
129
|
+
|
|
130
|
+
static std::shared_ptr<CWallet> walletTmp;
|
|
135
131
|
|
|
136
132
|
static fs::path GetPidFile(const ArgsManager& args)
|
|
137
133
|
{
|
|
@@ -245,8 +241,6 @@ void Shutdown(NodeContext& node)
|
|
|
245
241
|
DumpMempool(*node.mempool);
|
|
246
242
|
}
|
|
247
243
|
|
|
248
|
-
// Drop transactions we were still watching, and record fee estimations.
|
|
249
|
-
if (node.fee_estimator) node.fee_estimator->Flush();
|
|
250
244
|
|
|
251
245
|
// FlushStateToDisk generates a ChainStateFlushed callback, which we should avoid missing
|
|
252
246
|
if (node.chainman) {
|
|
@@ -306,7 +300,6 @@ void Shutdown(NodeContext& node)
|
|
|
306
300
|
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
|
307
301
|
init::UnsetGlobals();
|
|
308
302
|
node.mempool.reset();
|
|
309
|
-
node.fee_estimator.reset();
|
|
310
303
|
node.chainman.reset();
|
|
311
304
|
node.scheduler.reset();
|
|
312
305
|
|
|
@@ -420,9 +413,6 @@ void SetupServerArgs(ArgsManager& argsman)
|
|
|
420
413
|
-GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
421
414
|
argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
422
415
|
argsman.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
423
|
-
argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -coinstatsindex. "
|
|
424
|
-
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
|
|
425
|
-
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
426
416
|
argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
427
417
|
argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
428
418
|
argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
@@ -549,19 +539,15 @@ void SetupServerArgs(ArgsManager& argsman)
|
|
|
549
539
|
SetupChainParamsBaseOptions(argsman);
|
|
550
540
|
|
|
551
541
|
argsman.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
|
|
552
|
-
argsman.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
|
|
553
|
-
argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
|
|
554
542
|
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
555
543
|
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
556
544
|
argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
557
|
-
|
|
558
|
-
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
545
|
+
|
|
559
546
|
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
560
547
|
argsman.AddArg("-whitelistrelay", strprintf("Add 'relay' permission to whitelisted inbound peers with default permissions. This will accept relayed transactions even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
|
|
561
548
|
|
|
562
549
|
|
|
563
550
|
argsman.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
|
|
564
|
-
argsman.AddArg("-blockmintxfee=<amt>", strprintf("Set lowest fee rate (in %s/kvB) for transactions to be included in block creation. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
|
|
565
551
|
argsman.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::BLOCK_CREATION);
|
|
566
552
|
|
|
567
553
|
argsman.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
|
@@ -579,6 +565,7 @@ void SetupServerArgs(ArgsManager& argsman)
|
|
|
579
565
|
argsman.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
|
580
566
|
argsman.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
|
|
581
567
|
argsman.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
|
|
568
|
+
gArgs.AddArg("-nominting", "Disable minting of POS blocks", ArgsManager::ALLOW_ANY, OptionsCategory::BLOCK_CREATION);
|
|
582
569
|
|
|
583
570
|
#if HAVE_DECL_FORK
|
|
584
571
|
argsman.AddArg("-daemon", strprintf("Run in the background as a daemon and accept commands (default: %d)", DEFAULT_DAEMON), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
@@ -592,6 +579,15 @@ void SetupServerArgs(ArgsManager& argsman)
|
|
|
592
579
|
argsman.AddArg("-sandbox=<mode>", "Use the experimental syscall sandbox in the specified mode (-sandbox=log-and-abort or -sandbox=abort). Allow only expected syscalls to be used by bitcoind. Note that this is an experimental new feature that may cause bitcoind to exit or crash unexpectedly: use with caution. In the \"log-and-abort\" mode the invocation of an unexpected syscall results in a debug handler being invoked which will log the incident and terminate the program (without executing the unexpected syscall). In the \"abort\" mode the invocation of an unexpected syscall results in the entire process being killed immediately by the kernel without executing the unexpected syscall.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
593
580
|
#endif // USE_SYSCALL_SANDBOX
|
|
594
581
|
|
|
582
|
+
// peercoin parameters
|
|
583
|
+
gArgs.AddArg("-printstakemodifier", "Print stakemodifier selection parameters if debug is enabled", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
584
|
+
gArgs.AddArg("-printcoinstake", "Print coinstake if debug is enabled", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
585
|
+
gArgs.AddArg("-printcoinage", "Print coinage if debug is enabled", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
586
|
+
gArgs.AddArg("-printcreation", "Print coin creation if debug is enabled", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
|
|
587
|
+
|
|
588
|
+
gArgs.AddArg("-reservebalance=<amt>", "Reserve this many coins", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
589
|
+
gArgs.AddArg("-minting", "Enable minting (default: true)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
|
590
|
+
|
|
595
591
|
// Add the hidden options
|
|
596
592
|
argsman.AddHiddenArgs(hidden_args);
|
|
597
593
|
}
|
|
@@ -853,14 +849,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
853
849
|
nLocalServices = ServiceFlags(nLocalServices | NODE_COMPACT_FILTERS);
|
|
854
850
|
}
|
|
855
851
|
|
|
856
|
-
// if using block pruning, then disallow txindex and coinstatsindex
|
|
857
|
-
if (args.GetIntArg("-prune", 0)) {
|
|
858
|
-
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
|
|
859
|
-
return InitError(_("Prune mode is incompatible with -txindex."));
|
|
860
|
-
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX))
|
|
861
|
-
return InitError(_("Prune mode is incompatible with -coinstatsindex."));
|
|
862
|
-
}
|
|
863
|
-
|
|
864
852
|
// If -forcednsseed is set to true, ensure -dnsseed has not been set to false
|
|
865
853
|
if (args.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED) && !args.GetBoolArg("-dnsseed", DEFAULT_DNSSEED)){
|
|
866
854
|
return InitError(_("Cannot set -forcednsseed to true when setting -dnsseed to false."));
|
|
@@ -930,33 +918,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
930
918
|
int64_t nMempoolSizeMin = args.GetIntArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;
|
|
931
919
|
if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin)
|
|
932
920
|
return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0)));
|
|
933
|
-
// incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool
|
|
934
|
-
// and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting.
|
|
935
|
-
if (args.IsArgSet("-incrementalrelayfee")) {
|
|
936
|
-
if (std::optional<CAmount> inc_relay_fee = ParseMoney(args.GetArg("-incrementalrelayfee", ""))) {
|
|
937
|
-
::incrementalRelayFee = CFeeRate{inc_relay_fee.value()};
|
|
938
|
-
} else {
|
|
939
|
-
return InitError(AmountErrMsg("incrementalrelayfee", args.GetArg("-incrementalrelayfee", "")));
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
|
|
944
|
-
int64_t nPruneArg = args.GetIntArg("-prune", 0);
|
|
945
|
-
if (nPruneArg < 0) {
|
|
946
|
-
return InitError(_("Prune cannot be configured with a negative value."));
|
|
947
|
-
}
|
|
948
|
-
nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024;
|
|
949
|
-
if (nPruneArg == 1) { // manual pruning: -prune=1
|
|
950
|
-
LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n");
|
|
951
|
-
nPruneTarget = std::numeric_limits<uint64_t>::max();
|
|
952
|
-
fPruneMode = true;
|
|
953
|
-
} else if (nPruneTarget) {
|
|
954
|
-
if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) {
|
|
955
|
-
return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
|
|
956
|
-
}
|
|
957
|
-
LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024);
|
|
958
|
-
fPruneMode = true;
|
|
959
|
-
}
|
|
960
921
|
|
|
961
922
|
nConnectTimeout = args.GetIntArg("-timeout", DEFAULT_CONNECT_TIMEOUT);
|
|
962
923
|
if (nConnectTimeout <= 0) {
|
|
@@ -967,38 +928,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
967
928
|
if (peer_connect_timeout <= 0) {
|
|
968
929
|
return InitError(Untranslated("peertimeout cannot be configured with a negative value."));
|
|
969
930
|
}
|
|
970
|
-
|
|
971
|
-
if (args.IsArgSet("-minrelaytxfee")) {
|
|
972
|
-
if (std::optional<CAmount> min_relay_fee = ParseMoney(args.GetArg("-minrelaytxfee", ""))) {
|
|
973
|
-
// High fee check is done afterward in CWallet::Create()
|
|
974
|
-
::minRelayTxFee = CFeeRate{min_relay_fee.value()};
|
|
975
|
-
} else {
|
|
976
|
-
return InitError(AmountErrMsg("minrelaytxfee", args.GetArg("-minrelaytxfee", "")));
|
|
977
|
-
}
|
|
978
|
-
} else if (incrementalRelayFee > ::minRelayTxFee) {
|
|
979
|
-
// Allow only setting incrementalRelayFee to control both
|
|
980
|
-
::minRelayTxFee = incrementalRelayFee;
|
|
981
|
-
LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString());
|
|
982
|
-
}
|
|
983
|
-
|
|
984
|
-
// Sanity check argument for min fee for including tx in block
|
|
985
|
-
// TODO: Harmonize which arguments need sanity checking and where that happens
|
|
986
|
-
if (args.IsArgSet("-blockmintxfee")) {
|
|
987
|
-
if (!ParseMoney(args.GetArg("-blockmintxfee", ""))) {
|
|
988
|
-
return InitError(AmountErrMsg("blockmintxfee", args.GetArg("-blockmintxfee", "")));
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// Feerate used to define dust. Shouldn't be changed lightly as old
|
|
993
|
-
// implementations may inadvertently create non-standard transactions
|
|
994
|
-
if (args.IsArgSet("-dustrelayfee")) {
|
|
995
|
-
if (std::optional<CAmount> parsed = ParseMoney(args.GetArg("-dustrelayfee", ""))) {
|
|
996
|
-
dustRelayFee = CFeeRate{parsed.value()};
|
|
997
|
-
} else {
|
|
998
|
-
return InitError(AmountErrMsg("dustrelayfee", args.GetArg("-dustrelayfee", "")));
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
931
|
fRequireStandard = !args.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard());
|
|
1003
932
|
if (!chainparams.IsTestChain() && !fRequireStandard) {
|
|
1004
933
|
return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString()));
|
|
@@ -1024,7 +953,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
1024
953
|
return InitError(Untranslated("Unknown rpcserialversion requested."));
|
|
1025
954
|
|
|
1026
955
|
nMaxTipAge = args.GetIntArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
|
|
1027
|
-
|
|
1028
956
|
if (args.IsArgSet("-proxy") && args.GetArg("-proxy", "").empty()) {
|
|
1029
957
|
return InitError(_("No proxy server specified. Use -proxy=<ip> or -proxy=<ip:port>."));
|
|
1030
958
|
}
|
|
@@ -1065,7 +993,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
|
|
|
1065
993
|
|
|
1066
994
|
static bool LockDataDirectory(bool probeOnly)
|
|
1067
995
|
{
|
|
1068
|
-
// Make sure only a single
|
|
996
|
+
// Make sure only a single Peercoin process is using the data directory.
|
|
1069
997
|
fs::path datadir = gArgs.GetDataDirNet();
|
|
1070
998
|
if (!DirIsWritable(datadir)) {
|
|
1071
999
|
return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), fs::PathToString(datadir)));
|
|
@@ -1082,6 +1010,8 @@ bool AppInitSanityChecks()
|
|
|
1082
1010
|
|
|
1083
1011
|
init::SetGlobals();
|
|
1084
1012
|
|
|
1013
|
+
// peercoin: init hash seed
|
|
1014
|
+
peercoinRandseed = GetRand(1 << 30);
|
|
1085
1015
|
if (!init::SanityChecks()) {
|
|
1086
1016
|
return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME));
|
|
1087
1017
|
}
|
|
@@ -1135,9 +1065,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1135
1065
|
// Warn about relative -datadir path.
|
|
1136
1066
|
if (args.IsArgSet("-datadir") && !args.GetPathArg("-datadir").is_absolute()) {
|
|
1137
1067
|
LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */
|
|
1138
|
-
"current working directory '%s'. This is fragile, because if
|
|
1068
|
+
"current working directory '%s'. This is fragile, because if peercoin is started in the future "
|
|
1139
1069
|
"from a different location, it will be unable to locate the current data files. There could "
|
|
1140
|
-
"also be data loss if
|
|
1070
|
+
"also be data loss if peercoin is started while in a temporary directory.\n",
|
|
1141
1071
|
args.GetArg("-datadir", ""), fs::PathToString(fs::current_path()));
|
|
1142
1072
|
}
|
|
1143
1073
|
|
|
@@ -1262,14 +1192,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1262
1192
|
assert(!node.connman);
|
|
1263
1193
|
node.connman = std::make_unique<CConnman>(GetRand(std::numeric_limits<uint64_t>::max()), GetRand(std::numeric_limits<uint64_t>::max()), *node.addrman, args.GetBoolArg("-networkactive", true));
|
|
1264
1194
|
|
|
1265
|
-
assert(!node.fee_estimator);
|
|
1266
|
-
// Don't initialize fee estimation with old data if we don't relay transactions,
|
|
1267
|
-
// as they would never get updated.
|
|
1268
|
-
if (!ignores_incoming_txs) node.fee_estimator = std::make_unique<CBlockPolicyEstimator>();
|
|
1269
|
-
|
|
1270
1195
|
assert(!node.mempool);
|
|
1271
1196
|
int check_ratio = std::min<int>(std::max<int>(args.GetIntArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000);
|
|
1272
|
-
node.mempool = std::make_unique<CTxMemPool>(
|
|
1197
|
+
node.mempool = std::make_unique<CTxMemPool>(check_ratio);
|
|
1273
1198
|
|
|
1274
1199
|
assert(!node.chainman);
|
|
1275
1200
|
node.chainman = std::make_unique<ChainstateManager>();
|
|
@@ -1397,9 +1322,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1397
1322
|
int64_t nMempoolSizeMax = args.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
|
|
1398
1323
|
LogPrintf("Cache configuration:\n");
|
|
1399
1324
|
LogPrintf("* Using %.1f MiB for block index database\n", cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
|
|
1400
|
-
|
|
1401
|
-
LogPrintf("* Using %.1f MiB for transaction index database\n", cache_sizes.tx_index * (1.0 / 1024 / 1024));
|
|
1402
|
-
}
|
|
1325
|
+
LogPrintf("* Using %.1f MiB for transaction index database\n", cache_sizes.tx_index * (1.0 / 1024 / 1024));
|
|
1403
1326
|
for (BlockFilterType filter_type : g_enabled_filter_types) {
|
|
1404
1327
|
LogPrintf("* Using %.1f MiB for %s block filter index database\n",
|
|
1405
1328
|
cache_sizes.filter_index * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
|
|
@@ -1419,7 +1342,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1419
1342
|
maybe_load_error = LoadChainstate(fReset,
|
|
1420
1343
|
chainman,
|
|
1421
1344
|
Assert(node.mempool.get()),
|
|
1422
|
-
fPruneMode,
|
|
1423
1345
|
chainparams.GetConsensus(),
|
|
1424
1346
|
fReindexChainState,
|
|
1425
1347
|
cache_sizes.block_tree_db,
|
|
@@ -1446,9 +1368,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1446
1368
|
// If the loaded chain has a wrong genesis, bail out immediately
|
|
1447
1369
|
// (we're likely using a testnet datadir, or the other way around).
|
|
1448
1370
|
return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?"));
|
|
1449
|
-
case ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX:
|
|
1450
|
-
strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain");
|
|
1451
|
-
break;
|
|
1452
1371
|
case ChainstateLoadingError::ERROR_LOAD_GENESIS_BLOCK_FAILED:
|
|
1453
1372
|
strLoadError = _("Error initializing block database");
|
|
1454
1373
|
break;
|
|
@@ -1476,10 +1395,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1476
1395
|
try {
|
|
1477
1396
|
uiInterface.InitMessage(_("Verifying blocks…").translated);
|
|
1478
1397
|
auto check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
|
|
1479
|
-
if (fHavePruned && check_blocks > MIN_BLOCKS_TO_KEEP) {
|
|
1480
|
-
LogPrintf("Prune: pruned datadir may not have more than %d blocks; only checking available blocks\n",
|
|
1481
|
-
MIN_BLOCKS_TO_KEEP);
|
|
1482
|
-
}
|
|
1483
1398
|
maybe_verify_error = VerifyLoadedChainstate(chainman,
|
|
1484
1399
|
fReset,
|
|
1485
1400
|
fReindexChainState,
|
|
@@ -1540,15 +1455,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1540
1455
|
}
|
|
1541
1456
|
|
|
1542
1457
|
// ********************************************************* Step 8: start indexers
|
|
1543
|
-
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
|
1544
1458
|
if (const auto error{CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db))}) {
|
|
1545
1459
|
return InitError(*error);
|
|
1546
1460
|
}
|
|
1547
1461
|
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
}
|
|
1462
|
+
g_txindex = std::make_unique<TxIndex>(cache_sizes.tx_index, false, fReindex);
|
|
1463
|
+
if (!g_txindex->Start(chainman.ActiveChainstate())) {
|
|
1464
|
+
return false;
|
|
1552
1465
|
}
|
|
1553
1466
|
|
|
1554
1467
|
for (const auto& filter_type : g_enabled_filter_types) {
|
|
@@ -1574,19 +1487,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1574
1487
|
|
|
1575
1488
|
// ********************************************************* Step 10: data directory maintenance
|
|
1576
1489
|
|
|
1577
|
-
//
|
|
1578
|
-
// after
|
|
1579
|
-
|
|
1580
|
-
LogPrintf("Unsetting NODE_NETWORK on prune mode\n");
|
|
1581
|
-
nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK);
|
|
1582
|
-
if (!fReindex) {
|
|
1583
|
-
LOCK(cs_main);
|
|
1584
|
-
for (CChainState* chainstate : chainman.GetAll()) {
|
|
1585
|
-
uiInterface.InitMessage(_("Pruning blockstore…").translated);
|
|
1586
|
-
chainstate->PruneAndFlush();
|
|
1587
|
-
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1490
|
+
// Note that setting NODE_WITNESS is never required: the only downside from not
|
|
1491
|
+
// doing so is that after activation, no upgraded nodes will fetch from you.
|
|
1492
|
+
nLocalServices = ServiceFlags(nLocalServices | NODE_WITNESS);
|
|
1590
1493
|
|
|
1591
1494
|
// ********************************************************* Step 11: import blocks
|
|
1592
1495
|
|
|
@@ -1829,6 +1732,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|
|
1829
1732
|
|
|
1830
1733
|
#if HAVE_SYSTEM
|
|
1831
1734
|
StartupNotify(args);
|
|
1735
|
+
#endif
|
|
1736
|
+
#ifdef ENABLE_WALLET
|
|
1737
|
+
{
|
|
1738
|
+
// ppctodo: deal with multiple wallets
|
|
1739
|
+
if (node.wallet_loader->getWallets().size() && gArgs.GetBoolArg("-stakegen", true)) {
|
|
1740
|
+
walletTmp = std::shared_ptr<CWallet>(node.wallet_loader->getWallets()[0]->wallet());
|
|
1741
|
+
MintStake(walletTmp, node);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1832
1744
|
#endif
|
|
1833
1745
|
|
|
1834
1746
|
return true;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#ifndef BITCOIN_INTERFACES_CHAIN_H
|
|
6
6
|
#define BITCOIN_INTERFACES_CHAIN_H
|
|
7
7
|
|
|
8
|
+
#include <node/chainstate.h>
|
|
8
9
|
#include <primitives/transaction.h> // For CTransactionRef
|
|
9
10
|
#include <util/settings.h> // For util::SettingsValue
|
|
10
11
|
|
|
@@ -18,16 +19,13 @@
|
|
|
18
19
|
|
|
19
20
|
class ArgsManager;
|
|
20
21
|
class CBlock;
|
|
21
|
-
class CFeeRate;
|
|
22
22
|
class CRPCCommand;
|
|
23
23
|
class CScheduler;
|
|
24
24
|
class Coin;
|
|
25
25
|
class uint256;
|
|
26
26
|
enum class MemPoolRemovalReason;
|
|
27
|
-
enum class RBFTransactionState;
|
|
28
27
|
struct bilingual_str;
|
|
29
28
|
struct CBlockLocator;
|
|
30
|
-
struct FeeCalculation;
|
|
31
29
|
namespace node {
|
|
32
30
|
struct NodeContext;
|
|
33
31
|
} // namespace node
|
|
@@ -96,6 +94,8 @@ class Chain
|
|
|
96
94
|
public:
|
|
97
95
|
virtual ~Chain() {}
|
|
98
96
|
|
|
97
|
+
virtual ChainstateManager& chainman() = 0;
|
|
98
|
+
|
|
99
99
|
//! Get current chain height, not including genesis block (returns 0 if
|
|
100
100
|
//! chain only contains genesis block, nullopt if chain does not contain
|
|
101
101
|
//! any blocks)
|
|
@@ -104,8 +104,7 @@ public:
|
|
|
104
104
|
//! Get block hash. Height must be valid or this function will abort.
|
|
105
105
|
virtual uint256 getBlockHash(int height) = 0;
|
|
106
106
|
|
|
107
|
-
//! Check that the block is available on disk
|
|
108
|
-
//! pruned), and contains transactions.
|
|
107
|
+
//! Check that the block is available on disk, and contains transactions.
|
|
109
108
|
virtual bool haveBlockOnDisk(int height) = 0;
|
|
110
109
|
|
|
111
110
|
//! Get locator for the current chain tip.
|
|
@@ -158,9 +157,6 @@ public:
|
|
|
158
157
|
//! the height range from min_height to max_height, inclusive.
|
|
159
158
|
virtual bool hasBlocks(const uint256& block_hash, int min_height = 0, std::optional<int> max_height = {}) = 0;
|
|
160
159
|
|
|
161
|
-
//! Check if transaction is RBF opt in.
|
|
162
|
-
virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0;
|
|
163
|
-
|
|
164
160
|
//! Check if transaction is in mempool.
|
|
165
161
|
virtual bool isInMempool(const uint256& txid) = 0;
|
|
166
162
|
|
|
@@ -171,7 +167,6 @@ public:
|
|
|
171
167
|
//! amount specified by max_tx_fee, and broadcast to all peers if relay is set to true.
|
|
172
168
|
//! Return false if the transaction could not be added due to the fee or for another reason.
|
|
173
169
|
virtual bool broadcastTransaction(const CTransactionRef& tx,
|
|
174
|
-
const CAmount& max_tx_fee,
|
|
175
170
|
bool relay,
|
|
176
171
|
std::string& err_string) = 0;
|
|
177
172
|
|
|
@@ -186,27 +181,6 @@ public:
|
|
|
186
181
|
//! Check if transaction will pass the mempool's chain limits.
|
|
187
182
|
virtual bool checkChainLimits(const CTransactionRef& tx) = 0;
|
|
188
183
|
|
|
189
|
-
//! Estimate smart fee.
|
|
190
|
-
virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc = nullptr) = 0;
|
|
191
|
-
|
|
192
|
-
//! Fee estimator max target.
|
|
193
|
-
virtual unsigned int estimateMaxBlocks() = 0;
|
|
194
|
-
|
|
195
|
-
//! Mempool minimum fee.
|
|
196
|
-
virtual CFeeRate mempoolMinFee() = 0;
|
|
197
|
-
|
|
198
|
-
//! Relay current minimum fee (from -minrelaytxfee and -incrementalrelayfee settings).
|
|
199
|
-
virtual CFeeRate relayMinFee() = 0;
|
|
200
|
-
|
|
201
|
-
//! Relay incremental fee setting (-incrementalrelayfee), reflecting cost of relay.
|
|
202
|
-
virtual CFeeRate relayIncrementalFee() = 0;
|
|
203
|
-
|
|
204
|
-
//! Relay dust fee setting (-dustrelayfee), reflecting lowest rate it's economical to spend.
|
|
205
|
-
virtual CFeeRate relayDustFee() = 0;
|
|
206
|
-
|
|
207
|
-
//! Check if any block has been pruned.
|
|
208
|
-
virtual bool havePruned() = 0;
|
|
209
|
-
|
|
210
184
|
//! Check if the node is ready to broadcast transactions.
|
|
211
185
|
virtual bool isReadyToBroadcast() = 0;
|
|
212
186
|
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
#define BITCOIN_INTERFACES_INIT_H
|
|
7
7
|
|
|
8
8
|
#include <memory>
|
|
9
|
+
#include <interfaces/wallet.h>
|
|
10
|
+
|
|
11
|
+
using interfaces::WalletLoader;
|
|
9
12
|
|
|
10
13
|
namespace node {
|
|
11
14
|
struct NodeContext;
|
|
@@ -16,7 +19,7 @@ class Chain;
|
|
|
16
19
|
class Echo;
|
|
17
20
|
class Ipc;
|
|
18
21
|
class Node;
|
|
19
|
-
class WalletLoader;
|
|
22
|
+
//class WalletLoader;
|
|
20
23
|
|
|
21
24
|
//! Initial interface created when a process is first started, and used to give
|
|
22
25
|
//! and get access to other interfaces (Node, Chain, Wallet, etc).
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
#define BITCOIN_INTERFACES_NODE_H
|
|
7
7
|
|
|
8
8
|
#include <consensus/amount.h>
|
|
9
|
+
#include <node/chainstate.h>
|
|
9
10
|
#include <net.h> // For NodeId
|
|
10
11
|
#include <net_types.h> // For banmap_t
|
|
11
12
|
#include <netaddress.h> // For Network
|
|
12
13
|
#include <netbase.h> // For ConnectionDirection
|
|
13
14
|
#include <support/allocators/secure.h> // For SecureString
|
|
15
|
+
#include <util/ui_change_type.h>
|
|
14
16
|
#include <util/translation.h>
|
|
15
17
|
|
|
16
18
|
#include <functional>
|
|
@@ -22,7 +24,6 @@
|
|
|
22
24
|
#include <vector>
|
|
23
25
|
|
|
24
26
|
class BanMan;
|
|
25
|
-
class CFeeRate;
|
|
26
27
|
class CNodeStats;
|
|
27
28
|
class Coin;
|
|
28
29
|
class RPCTimerInterface;
|
|
@@ -70,6 +71,8 @@ class Node
|
|
|
70
71
|
public:
|
|
71
72
|
virtual ~Node() {}
|
|
72
73
|
|
|
74
|
+
virtual ChainstateManager& chainman() = 0;
|
|
75
|
+
|
|
73
76
|
//! Init logging.
|
|
74
77
|
virtual void initLogging() = 0;
|
|
75
78
|
|
|
@@ -170,9 +173,6 @@ public:
|
|
|
170
173
|
//! Get network active.
|
|
171
174
|
virtual bool getNetworkActive() = 0;
|
|
172
175
|
|
|
173
|
-
//! Get dust relay fee.
|
|
174
|
-
virtual CFeeRate getDustRelayFee() = 0;
|
|
175
|
-
|
|
176
176
|
//! Execute rpc command.
|
|
177
177
|
virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0;
|
|
178
178
|
|
|
@@ -189,7 +189,7 @@ public:
|
|
|
189
189
|
virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0;
|
|
190
190
|
|
|
191
191
|
//! Broadcast transaction.
|
|
192
|
-
virtual TransactionError broadcastTransaction(CTransactionRef tx,
|
|
192
|
+
virtual TransactionError broadcastTransaction(CTransactionRef tx, std::string& err_string) = 0;
|
|
193
193
|
|
|
194
194
|
//! Get wallet loader.
|
|
195
195
|
virtual WalletLoader& walletLoader() = 0;
|
|
@@ -227,7 +227,7 @@ public:
|
|
|
227
227
|
virtual std::unique_ptr<Handler> handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) = 0;
|
|
228
228
|
|
|
229
229
|
//! Register handler for notify alert messages.
|
|
230
|
-
using NotifyAlertChangedFn = std::function<void()>;
|
|
230
|
+
using NotifyAlertChangedFn = std::function<void(const uint256 &hash, ChangeType status)>;
|
|
231
231
|
virtual std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) = 0;
|
|
232
232
|
|
|
233
233
|
//! Register handler for ban list messages.
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
#include <utility>
|
|
25
25
|
#include <vector>
|
|
26
26
|
|
|
27
|
-
class CFeeRate;
|
|
28
27
|
class CKey;
|
|
29
28
|
enum class FeeReason;
|
|
30
29
|
enum class OutputType;
|
|
@@ -156,26 +155,6 @@ public:
|
|
|
156
155
|
//! Abandon transaction.
|
|
157
156
|
virtual bool abandonTransaction(const uint256& txid) = 0;
|
|
158
157
|
|
|
159
|
-
//! Return whether transaction can be bumped.
|
|
160
|
-
virtual bool transactionCanBeBumped(const uint256& txid) = 0;
|
|
161
|
-
|
|
162
|
-
//! Create bump transaction.
|
|
163
|
-
virtual bool createBumpTransaction(const uint256& txid,
|
|
164
|
-
const wallet::CCoinControl& coin_control,
|
|
165
|
-
std::vector<bilingual_str>& errors,
|
|
166
|
-
CAmount& old_fee,
|
|
167
|
-
CAmount& new_fee,
|
|
168
|
-
CMutableTransaction& mtx) = 0;
|
|
169
|
-
|
|
170
|
-
//! Sign bump transaction.
|
|
171
|
-
virtual bool signBumpTransaction(CMutableTransaction& mtx) = 0;
|
|
172
|
-
|
|
173
|
-
//! Commit bump transaction.
|
|
174
|
-
virtual bool commitBumpTransaction(const uint256& txid,
|
|
175
|
-
CMutableTransaction&& mtx,
|
|
176
|
-
std::vector<bilingual_str>& errors,
|
|
177
|
-
uint256& bumped_txid) = 0;
|
|
178
|
-
|
|
179
158
|
//! Get a transaction.
|
|
180
159
|
virtual CTransactionRef getTx(const uint256& txid) = 0;
|
|
181
160
|
|
|
@@ -237,7 +216,7 @@ public:
|
|
|
237
216
|
|
|
238
217
|
//! Return wallet transaction output information.
|
|
239
218
|
virtual std::vector<WalletTxOut> getCoins(const std::vector<COutPoint>& outputs) = 0;
|
|
240
|
-
|
|
219
|
+
/*
|
|
241
220
|
//! Get required fee.
|
|
242
221
|
virtual CAmount getRequiredFee(unsigned int tx_bytes) = 0;
|
|
243
222
|
|
|
@@ -249,7 +228,7 @@ public:
|
|
|
249
228
|
|
|
250
229
|
//! Get tx confirm target.
|
|
251
230
|
virtual unsigned int getConfirmTarget() = 0;
|
|
252
|
-
|
|
231
|
+
*/
|
|
253
232
|
// Return whether HD enabled.
|
|
254
233
|
virtual bool hdEnabled() = 0;
|
|
255
234
|
|
|
@@ -268,9 +247,6 @@ public:
|
|
|
268
247
|
// Get default address type.
|
|
269
248
|
virtual OutputType getDefaultAddressType() = 0;
|
|
270
249
|
|
|
271
|
-
//! Get max tx fee.
|
|
272
|
-
virtual CAmount getDefaultMaxTxFee() = 0;
|
|
273
|
-
|
|
274
250
|
// Remove wallet.
|
|
275
251
|
virtual void remove() = 0;
|
|
276
252
|
|
|
@@ -311,6 +287,9 @@ public:
|
|
|
311
287
|
|
|
312
288
|
//! Return pointer to internal wallet class, useful for testing.
|
|
313
289
|
virtual wallet::CWallet* wallet() { return nullptr; }
|
|
290
|
+
// peercoin
|
|
291
|
+
virtual void relockWalletAfterDuration(int nDuration) = 0;
|
|
292
|
+
virtual std::shared_ptr<wallet::CWallet> getWallet() = 0;
|
|
314
293
|
};
|
|
315
294
|
|
|
316
295
|
//! Wallet chain client that in addition to having chain client methods for
|
|
@@ -365,6 +344,7 @@ struct WalletAddress
|
|
|
365
344
|
struct WalletBalances
|
|
366
345
|
{
|
|
367
346
|
CAmount balance = 0;
|
|
347
|
+
CAmount stake = 0;
|
|
368
348
|
CAmount unconfirmed_balance = 0;
|
|
369
349
|
CAmount immature_balance = 0;
|
|
370
350
|
bool have_watch_only = false;
|
|
@@ -374,7 +354,7 @@ struct WalletBalances
|
|
|
374
354
|
|
|
375
355
|
bool balanceChanged(const WalletBalances& prev) const
|
|
376
356
|
{
|
|
377
|
-
return balance != prev.balance || unconfirmed_balance != prev.unconfirmed_balance ||
|
|
357
|
+
return balance != prev.balance || stake != prev.stake || unconfirmed_balance != prev.unconfirmed_balance ||
|
|
378
358
|
immature_balance != prev.immature_balance || watch_only_balance != prev.watch_only_balance ||
|
|
379
359
|
unconfirmed_watch_only_balance != prev.unconfirmed_watch_only_balance ||
|
|
380
360
|
immature_watch_only_balance != prev.immature_watch_only_balance;
|
|
@@ -395,6 +375,7 @@ struct WalletTx
|
|
|
395
375
|
int64_t time;
|
|
396
376
|
std::map<std::string, std::string> value_map;
|
|
397
377
|
bool is_coinbase;
|
|
378
|
+
bool is_coinstake;
|
|
398
379
|
};
|
|
399
380
|
|
|
400
381
|
//! Updated transaction status.
|
|
@@ -408,6 +389,7 @@ struct WalletTxStatus
|
|
|
408
389
|
bool is_trusted;
|
|
409
390
|
bool is_abandoned;
|
|
410
391
|
bool is_coinbase;
|
|
392
|
+
bool is_coinstake;
|
|
411
393
|
bool is_in_main_chain;
|
|
412
394
|
};
|
|
413
395
|
|
|
@@ -0,0 +1,725 @@
|
|
|
1
|
+
// Copyright (c) 2012-2023 The Peercoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
|
|
5
|
+
#include <kernel.h>
|
|
6
|
+
#include <chainparams.h>
|
|
7
|
+
#include <validation.h>
|
|
8
|
+
#include <streams.h>
|
|
9
|
+
#include <timedata.h>
|
|
10
|
+
#include <bignum.h>
|
|
11
|
+
#include <txdb.h>
|
|
12
|
+
#include <consensus/validation.h>
|
|
13
|
+
#include <validation.h>
|
|
14
|
+
#include <random.h>
|
|
15
|
+
#include <script/interpreter.h>
|
|
16
|
+
|
|
17
|
+
#include <index/txindex.h>
|
|
18
|
+
|
|
19
|
+
#include <boost/assign/list_of.hpp>
|
|
20
|
+
|
|
21
|
+
using namespace std;
|
|
22
|
+
|
|
23
|
+
// Protocol switch time of v0.3 kernel protocol
|
|
24
|
+
unsigned int nProtocolV03SwitchTime = 1363800000;
|
|
25
|
+
unsigned int nProtocolV03TestSwitchTime = 1359781000;
|
|
26
|
+
// Protocol switch time of v0.4 kernel protocol
|
|
27
|
+
unsigned int nProtocolV04SwitchTime = 1399300000;
|
|
28
|
+
unsigned int nProtocolV04TestSwitchTime = 1395700000;
|
|
29
|
+
// Protocol switch time of v0.5 kernel protocol
|
|
30
|
+
unsigned int nProtocolV05SwitchTime = 1461700000;
|
|
31
|
+
unsigned int nProtocolV05TestSwitchTime = 1447700000;
|
|
32
|
+
// Protocol switch time of v0.6 kernel protocol
|
|
33
|
+
// supermajority hardfork: actual fork will happen later than switch time
|
|
34
|
+
const unsigned int nProtocolV06SwitchTime = 1513050000; // Tue 12 Dec 03:40:00 UTC 2017
|
|
35
|
+
const unsigned int nProtocolV06TestSwitchTime = 1508198400; // Tue 17 Oct 00:00:00 UTC 2017
|
|
36
|
+
// Protocol switch time for 0.7 kernel protocol
|
|
37
|
+
const unsigned int nProtocolV07SwitchTime = 1552392000; // Tue 12 Mar 12:00:00 UTC 2019
|
|
38
|
+
const unsigned int nProtocolV07TestSwitchTime = 1541505600; // Tue 06 Nov 12:00:00 UTC 2018
|
|
39
|
+
// Switch time for new BIPs from bitcoin 0.16.x
|
|
40
|
+
const uint32_t nBTC16BIPsSwitchTime = 1569931200; // Tue 01 Oct 12:00:00 UTC 2019
|
|
41
|
+
const uint32_t nBTC16BIPsTestSwitchTime = 1554811200; // Tue 09 Apr 12:00:00 UTC 2019
|
|
42
|
+
// Protocol switch time for v0.9 kernel protocol
|
|
43
|
+
const unsigned int nProtocolV09SwitchTime = 1591617600; // Mon 8 Jun 12:00:00 UTC 2020
|
|
44
|
+
const unsigned int nProtocolV09TestSwitchTime = 1581940800; // Mon 17 Feb 12:00:00 UTC 2020
|
|
45
|
+
// Protocol switch time for v10 kernel protocol
|
|
46
|
+
const unsigned int nProtocolV10SwitchTime = 1635768000; // Mon 1 Nov 12:00:00 UTC 2021
|
|
47
|
+
const unsigned int nProtocolV10TestSwitchTime = 1625140800; // Thu 1 Jul 12:00:00 UTC 2021
|
|
48
|
+
// Protocol switch time for v12 kernel protocol
|
|
49
|
+
const unsigned int nProtocolV12SwitchTime = 1681732800; // Mon 17 Apr 12:00:00 UTC 2023
|
|
50
|
+
const unsigned int nProtocolV12TestSwitchTime = 1669636800; // Mon 28 Nov 12:00:00 UTC 2022
|
|
51
|
+
|
|
52
|
+
// Hard checkpoints of stake modifiers to ensure they are deterministic
|
|
53
|
+
static std::map<int, unsigned int> mapStakeModifierCheckpoints =
|
|
54
|
+
boost::assign::map_list_of
|
|
55
|
+
( 0, 0x0e00670bu )
|
|
56
|
+
( 19080, 0xad4e4d29u )
|
|
57
|
+
( 30583, 0xdc7bf136u )
|
|
58
|
+
( 99999, 0xf555cfd2u )
|
|
59
|
+
(219999, 0x91b7444du )
|
|
60
|
+
(336000, 0x6c3c8048u )
|
|
61
|
+
(371850, 0x9b850bdfu )
|
|
62
|
+
(407813, 0x46fe50b5u )
|
|
63
|
+
(443561, 0x114a6e38u )
|
|
64
|
+
(455470, 0x9b7af181u )
|
|
65
|
+
(479189, 0xe04fb8e0u )
|
|
66
|
+
(504051, 0x459f5a16u )
|
|
67
|
+
(589659, 0xbd02492au )
|
|
68
|
+
;
|
|
69
|
+
|
|
70
|
+
static std::map<int, unsigned int> mapStakeModifierTestnetCheckpoints =
|
|
71
|
+
boost::assign::map_list_of
|
|
72
|
+
( 0, 0x0e00670bu )
|
|
73
|
+
( 19080, 0x3711dc3au )
|
|
74
|
+
( 30583, 0xb480fadeu )
|
|
75
|
+
( 99999, 0x9a62eaecu )
|
|
76
|
+
(219999, 0xeafe96c3u )
|
|
77
|
+
(336000, 0x8330dc09u )
|
|
78
|
+
(372751, 0xafb94e2fu )
|
|
79
|
+
(382019, 0x7f5cf5ebu )
|
|
80
|
+
(408500, 0x68cadee2u )
|
|
81
|
+
(412691, 0x93138e67u )
|
|
82
|
+
(441299, 0x03e195cbu )
|
|
83
|
+
(442735, 0xe42d94feu )
|
|
84
|
+
;
|
|
85
|
+
|
|
86
|
+
// Whether the given coinstake is subject to new v0.3 protocol
|
|
87
|
+
bool IsProtocolV03(unsigned int nTimeCoinStake)
|
|
88
|
+
{
|
|
89
|
+
return (nTimeCoinStake >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV03TestSwitchTime : nProtocolV03SwitchTime));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Whether the given block is subject to new v0.4 protocol
|
|
93
|
+
bool IsProtocolV04(unsigned int nTimeBlock)
|
|
94
|
+
{
|
|
95
|
+
return (nTimeBlock >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV04TestSwitchTime : nProtocolV04SwitchTime));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Whether the given transaction is subject to new v0.5 protocol
|
|
99
|
+
bool IsProtocolV05(unsigned int nTimeTx)
|
|
100
|
+
{
|
|
101
|
+
return (nTimeTx >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV05TestSwitchTime : nProtocolV05SwitchTime));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Whether a given block is subject to new v0.6 protocol
|
|
105
|
+
// Test against previous block index! (always available)
|
|
106
|
+
bool IsProtocolV06(const CBlockIndex* pindexPrev)
|
|
107
|
+
{
|
|
108
|
+
if (pindexPrev->nTime < (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV06TestSwitchTime : nProtocolV06SwitchTime))
|
|
109
|
+
return false;
|
|
110
|
+
|
|
111
|
+
// if 900 of the last 1,000 blocks are version 2 or greater (90/100 if testnet):
|
|
112
|
+
// Soft-forking PoS can be dangerous if the super majority is too low
|
|
113
|
+
// The stake majority will decrease after the fork
|
|
114
|
+
// since only coindays of updated nodes will get destroyed.
|
|
115
|
+
if ((Params().NetworkIDString() == CBaseChainParams::MAIN && IsSuperMajority(2, pindexPrev, 900, 1000)) ||
|
|
116
|
+
(Params().NetworkIDString() != CBaseChainParams::MAIN && IsSuperMajority(2, pindexPrev, 90, 100)))
|
|
117
|
+
return true;
|
|
118
|
+
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Whether a given transaction is subject to new v0.7 protocol
|
|
123
|
+
bool IsProtocolV07(unsigned int nTimeTx)
|
|
124
|
+
{
|
|
125
|
+
bool fTestNet = Params().NetworkIDString() != CBaseChainParams::MAIN;
|
|
126
|
+
return (nTimeTx >= (fTestNet? nProtocolV07TestSwitchTime : nProtocolV07SwitchTime));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
bool IsBTC16BIPsEnabled(uint32_t nTimeTx)
|
|
130
|
+
{
|
|
131
|
+
bool fTestNet = Params().NetworkIDString() != CBaseChainParams::MAIN;
|
|
132
|
+
return (nTimeTx >= (fTestNet? nBTC16BIPsTestSwitchTime : nBTC16BIPsSwitchTime));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Whether a given timestamp is subject to new v0.9 protocol
|
|
136
|
+
bool IsProtocolV09(unsigned int nTime)
|
|
137
|
+
{
|
|
138
|
+
return (nTime >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV09TestSwitchTime : nProtocolV09SwitchTime));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Whether a given timestamp is subject to new v10 protocol
|
|
142
|
+
bool IsProtocolV10(unsigned int nTime)
|
|
143
|
+
{
|
|
144
|
+
return (nTime >= (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV10TestSwitchTime : nProtocolV10SwitchTime));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Whether a given timestamp is subject to new v10 protocol
|
|
148
|
+
bool IsProtocolV12(const CBlockIndex* pindexPrev)
|
|
149
|
+
{
|
|
150
|
+
if (pindexPrev->nTime < (Params().NetworkIDString() != CBaseChainParams::MAIN ? nProtocolV12TestSwitchTime : nProtocolV12SwitchTime))
|
|
151
|
+
return false;
|
|
152
|
+
|
|
153
|
+
if ((Params().NetworkIDString() == CBaseChainParams::MAIN && IsSuperMajority(4, pindexPrev, 900, 1000)) ||
|
|
154
|
+
(Params().NetworkIDString() != CBaseChainParams::MAIN && IsSuperMajority(4, pindexPrev, 90, 100)))
|
|
155
|
+
return true;
|
|
156
|
+
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get the last stake modifier and its generation time from a given block
|
|
161
|
+
static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64_t& nStakeModifier, int64_t& nModifierTime)
|
|
162
|
+
{
|
|
163
|
+
if (!pindex)
|
|
164
|
+
return error("GetLastStakeModifier: null pindex");
|
|
165
|
+
while (pindex && pindex->pprev && !pindex->GeneratedStakeModifier())
|
|
166
|
+
pindex = pindex->pprev;
|
|
167
|
+
if (!pindex->GeneratedStakeModifier())
|
|
168
|
+
return error("GetLastStakeModifier: no generation at genesis block");
|
|
169
|
+
nStakeModifier = pindex->nStakeModifier;
|
|
170
|
+
nModifierTime = pindex->GetBlockTime();
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Get selection interval section (in seconds)
|
|
175
|
+
static int64_t GetStakeModifierSelectionIntervalSection(int nSection)
|
|
176
|
+
{
|
|
177
|
+
assert (nSection >= 0 && nSection < 64);
|
|
178
|
+
return (Params().GetConsensus().nModifierInterval * 63 / (63 + ((63 - nSection) * (MODIFIER_INTERVAL_RATIO - 1))));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Get stake modifier selection interval (in seconds)
|
|
182
|
+
static int64_t GetStakeModifierSelectionInterval()
|
|
183
|
+
{
|
|
184
|
+
int64_t nSelectionInterval = 0;
|
|
185
|
+
for (int nSection=0; nSection<64; nSection++)
|
|
186
|
+
nSelectionInterval += GetStakeModifierSelectionIntervalSection(nSection);
|
|
187
|
+
return nSelectionInterval;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// select a block from the candidate blocks in vSortedByTimestamp, excluding
|
|
191
|
+
// already selected blocks in vSelectedBlocks, and with timestamp up to
|
|
192
|
+
// nSelectionIntervalStop.
|
|
193
|
+
static bool SelectBlockFromCandidates(
|
|
194
|
+
vector<pair<int64_t, uint256> >& vSortedByTimestamp,
|
|
195
|
+
map<uint256, const CBlockIndex*>& mapSelectedBlocks,
|
|
196
|
+
int64_t nSelectionIntervalStop, uint64_t nStakeModifierPrev,
|
|
197
|
+
const CBlockIndex** pindexSelected,
|
|
198
|
+
CChainState& chainstate)
|
|
199
|
+
{
|
|
200
|
+
bool fSelected = false;
|
|
201
|
+
arith_uint256 hashBest = 0;
|
|
202
|
+
*pindexSelected = (const CBlockIndex*) 0;
|
|
203
|
+
for (const auto& item : vSortedByTimestamp)
|
|
204
|
+
{
|
|
205
|
+
/*
|
|
206
|
+
if (!chainstate.BlockIndex().count(item.second))
|
|
207
|
+
return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString());
|
|
208
|
+
const CBlockIndex* pindex = pindexSelected.BlockIndex()[item.second];
|
|
209
|
+
*/
|
|
210
|
+
const CBlockIndex* pindex = chainstate.m_blockman.LookupBlockIndex(item.second);
|
|
211
|
+
if (!pindex)
|
|
212
|
+
return error("SelectBlockFromCandidates: failed to find block index for candidate block %s", item.second.ToString());
|
|
213
|
+
|
|
214
|
+
if (fSelected && pindex->GetBlockTime() > nSelectionIntervalStop)
|
|
215
|
+
break;
|
|
216
|
+
if (mapSelectedBlocks.count(pindex->GetBlockHash()) > 0)
|
|
217
|
+
continue;
|
|
218
|
+
// compute the selection hash by hashing its proof-hash and the
|
|
219
|
+
// previous proof-of-stake modifier
|
|
220
|
+
uint256 hashProof = pindex->IsProofOfStake()? pindex->hashProofOfStake : pindex->GetBlockHash();
|
|
221
|
+
CDataStream ss(SER_GETHASH, 0);
|
|
222
|
+
ss << hashProof << nStakeModifierPrev;
|
|
223
|
+
arith_uint256 hashSelection = UintToArith256(Hash(ss));
|
|
224
|
+
// the selection hash is divided by 2**32 so that proof-of-stake block
|
|
225
|
+
// is always favored over proof-of-work block. this is to preserve
|
|
226
|
+
// the energy efficiency property
|
|
227
|
+
if (pindex->IsProofOfStake())
|
|
228
|
+
hashSelection >>= 32;
|
|
229
|
+
if (fSelected && hashSelection < hashBest)
|
|
230
|
+
{
|
|
231
|
+
hashBest = hashSelection;
|
|
232
|
+
*pindexSelected = (const CBlockIndex*) pindex;
|
|
233
|
+
}
|
|
234
|
+
else if (!fSelected)
|
|
235
|
+
{
|
|
236
|
+
fSelected = true;
|
|
237
|
+
hashBest = hashSelection;
|
|
238
|
+
*pindexSelected = (const CBlockIndex*) pindex;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
|
|
242
|
+
LogPrintf("SelectBlockFromCandidates: selection hash=%s\n", hashBest.ToString());
|
|
243
|
+
return fSelected;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Stake Modifier (hash modifier of proof-of-stake):
|
|
247
|
+
// The purpose of stake modifier is to prevent a txout (coin) owner from
|
|
248
|
+
// computing future proof-of-stake generated by this txout at the time
|
|
249
|
+
// of transaction confirmation. To meet kernel protocol, the txout
|
|
250
|
+
// must hash with a future stake modifier to generate the proof.
|
|
251
|
+
// Stake modifier consists of bits each of which is contributed from a
|
|
252
|
+
// selected block of a given block group in the past.
|
|
253
|
+
// The selection of a block is based on a hash of the block's proof-hash and
|
|
254
|
+
// the previous stake modifier.
|
|
255
|
+
// Stake modifier is recomputed at a fixed time interval instead of every
|
|
256
|
+
// block. This is to make it difficult for an attacker to gain control of
|
|
257
|
+
// additional bits in the stake modifier, even after generating a chain of
|
|
258
|
+
// blocks.
|
|
259
|
+
bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t &nStakeModifier, bool& fGeneratedStakeModifier, CChainState& chainstate)
|
|
260
|
+
{
|
|
261
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
262
|
+
const CBlockIndex* pindexPrev = pindexCurrent->pprev;
|
|
263
|
+
nStakeModifier = 0;
|
|
264
|
+
fGeneratedStakeModifier = false;
|
|
265
|
+
if (!pindexPrev)
|
|
266
|
+
{
|
|
267
|
+
fGeneratedStakeModifier = true;
|
|
268
|
+
return true; // genesis block's modifier is 0
|
|
269
|
+
}
|
|
270
|
+
// First find current stake modifier and its generation block time
|
|
271
|
+
// if it's not old enough, return the same stake modifier
|
|
272
|
+
int64_t nModifierTime = 0;
|
|
273
|
+
if (!GetLastStakeModifier(pindexPrev, nStakeModifier, nModifierTime))
|
|
274
|
+
return error("ComputeNextStakeModifier: unable to get last modifier");
|
|
275
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
276
|
+
LogPrintf("ComputeNextStakeModifier: prev modifier=0x%016x time=%s epoch=%u\n", nStakeModifier, FormatISO8601DateTime(nModifierTime), (unsigned int)nModifierTime);
|
|
277
|
+
if (nModifierTime / params.nModifierInterval >= pindexPrev->GetBlockTime() / params.nModifierInterval)
|
|
278
|
+
{
|
|
279
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
280
|
+
LogPrintf("ComputeNextStakeModifier: no new interval keep current modifier: pindexPrev nHeight=%d nTime=%u\n", pindexPrev->nHeight, (unsigned int)pindexPrev->GetBlockTime());
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
if (nModifierTime / params.nModifierInterval >= pindexCurrent->GetBlockTime() / params.nModifierInterval)
|
|
284
|
+
{
|
|
285
|
+
// v0.4+ requires current block timestamp also be in a different modifier interval
|
|
286
|
+
if (IsProtocolV04(pindexCurrent->nTime))
|
|
287
|
+
{
|
|
288
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
289
|
+
LogPrintf("ComputeNextStakeModifier: (v0.4+) no new interval keep current modifier: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
else
|
|
293
|
+
{
|
|
294
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
295
|
+
LogPrintf("ComputeNextStakeModifier: v0.3 modifier at block %s not meeting v0.4+ protocol: pindexCurrent nHeight=%d nTime=%u\n", pindexCurrent->GetBlockHash().ToString(), pindexCurrent->nHeight, (unsigned int)pindexCurrent->GetBlockTime());
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Sort candidate blocks by timestamp
|
|
300
|
+
vector<pair<int64_t, uint256> > vSortedByTimestamp;
|
|
301
|
+
vSortedByTimestamp.reserve(64 * params.nModifierInterval / params.nStakeTargetSpacing);
|
|
302
|
+
int64_t nSelectionInterval = GetStakeModifierSelectionInterval();
|
|
303
|
+
int64_t nSelectionIntervalStart = (pindexPrev->GetBlockTime() / params.nModifierInterval) * params.nModifierInterval - nSelectionInterval;
|
|
304
|
+
const CBlockIndex* pindex = pindexPrev;
|
|
305
|
+
while (pindex && pindex->GetBlockTime() >= nSelectionIntervalStart)
|
|
306
|
+
{
|
|
307
|
+
vSortedByTimestamp.push_back(make_pair(pindex->GetBlockTime(), pindex->GetBlockHash()));
|
|
308
|
+
pindex = pindex->pprev;
|
|
309
|
+
}
|
|
310
|
+
int nHeightFirstCandidate = pindex ? (pindex->nHeight + 1) : 0;
|
|
311
|
+
|
|
312
|
+
// Shuffle before sort
|
|
313
|
+
for(int i = vSortedByTimestamp.size() - 1; i > 1; --i)
|
|
314
|
+
std::swap(vSortedByTimestamp[i], vSortedByTimestamp[GetRand(i)]);
|
|
315
|
+
|
|
316
|
+
sort(vSortedByTimestamp.begin(), vSortedByTimestamp.end(), [] (const pair<int64_t, uint256> &a, const pair<int64_t, uint256> &b)
|
|
317
|
+
{
|
|
318
|
+
if (a.first != b.first)
|
|
319
|
+
return a.first < b.first;
|
|
320
|
+
// Timestamp equals - compare block hashes
|
|
321
|
+
const uint32_t *pa = a.second.GetDataPtr();
|
|
322
|
+
const uint32_t *pb = b.second.GetDataPtr();
|
|
323
|
+
int cnt = 256 / 32;
|
|
324
|
+
do {
|
|
325
|
+
--cnt;
|
|
326
|
+
if (pa[cnt] != pb[cnt])
|
|
327
|
+
return pa[cnt] < pb[cnt];
|
|
328
|
+
} while(cnt);
|
|
329
|
+
return false; // Elements are equal
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Select 64 blocks from candidate blocks to generate stake modifier
|
|
333
|
+
uint64_t nStakeModifierNew = 0;
|
|
334
|
+
int64_t nSelectionIntervalStop = nSelectionIntervalStart;
|
|
335
|
+
map<uint256, const CBlockIndex*> mapSelectedBlocks;
|
|
336
|
+
for (int nRound=0; nRound<min(64, (int)vSortedByTimestamp.size()); nRound++)
|
|
337
|
+
{
|
|
338
|
+
// add an interval section to the current selection round
|
|
339
|
+
nSelectionIntervalStop += GetStakeModifierSelectionIntervalSection(nRound);
|
|
340
|
+
// select a block from the candidates of current round
|
|
341
|
+
if (!SelectBlockFromCandidates(vSortedByTimestamp, mapSelectedBlocks, nSelectionIntervalStop, nStakeModifier, &pindex, chainstate))
|
|
342
|
+
return error("ComputeNextStakeModifier: unable to select block at round %d", nRound);
|
|
343
|
+
// write the entropy bit of the selected block
|
|
344
|
+
nStakeModifierNew |= (((uint64_t)pindex->GetStakeEntropyBit()) << nRound);
|
|
345
|
+
// add the selected block from candidates to selected list
|
|
346
|
+
mapSelectedBlocks.insert(make_pair(pindex->GetBlockHash(), pindex));
|
|
347
|
+
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
|
|
348
|
+
LogPrintf("ComputeNextStakeModifier: selected round %d stop=%s height=%d bit=%d\n",
|
|
349
|
+
nRound, FormatISO8601DateTime(nSelectionIntervalStop), pindex->nHeight, pindex->GetStakeEntropyBit());
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Print selection map for visualization of the selected blocks
|
|
353
|
+
if (gArgs.GetBoolArg("-debug", false) && gArgs.GetBoolArg("-printstakemodifier", false))
|
|
354
|
+
{
|
|
355
|
+
string strSelectionMap = "";
|
|
356
|
+
// '-' indicates proof-of-work blocks not selected
|
|
357
|
+
strSelectionMap.insert(0, pindexPrev->nHeight - nHeightFirstCandidate + 1, '-');
|
|
358
|
+
pindex = pindexPrev;
|
|
359
|
+
while (pindex && pindex->nHeight >= nHeightFirstCandidate)
|
|
360
|
+
{
|
|
361
|
+
// '=' indicates proof-of-stake blocks not selected
|
|
362
|
+
if (pindex->IsProofOfStake())
|
|
363
|
+
strSelectionMap.replace(pindex->nHeight - nHeightFirstCandidate, 1, "=");
|
|
364
|
+
pindex = pindex->pprev;
|
|
365
|
+
}
|
|
366
|
+
for (const auto& item : mapSelectedBlocks)
|
|
367
|
+
{
|
|
368
|
+
// 'S' indicates selected proof-of-stake blocks
|
|
369
|
+
// 'W' indicates selected proof-of-work blocks
|
|
370
|
+
strSelectionMap.replace(item.second->nHeight - nHeightFirstCandidate, 1, item.second->IsProofOfStake()? "S" : "W");
|
|
371
|
+
}
|
|
372
|
+
LogPrintf("ComputeNextStakeModifier: selection height [%d, %d] map %s\n", nHeightFirstCandidate, pindexPrev->nHeight, strSelectionMap);
|
|
373
|
+
}
|
|
374
|
+
if (gArgs.GetBoolArg("-debug", false))
|
|
375
|
+
LogPrintf("ComputeNextStakeModifier: new modifier=0x%016x time=%s\n", nStakeModifierNew, FormatISO8601DateTime(pindexPrev->GetBlockTime()));
|
|
376
|
+
|
|
377
|
+
nStakeModifier = nStakeModifierNew;
|
|
378
|
+
fGeneratedStakeModifier = true;
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// V0.5: Stake modifier used to hash for a stake kernel is chosen as the stake
|
|
383
|
+
// modifier that is (nStakeMinAge minus a selection interval) earlier than the
|
|
384
|
+
// stake, thus at least a selection interval later than the coin generating the
|
|
385
|
+
// kernel, as the generating coin is from at least nStakeMinAge ago.
|
|
386
|
+
static bool GetKernelStakeModifierV05(CBlockIndex* pindexPrev, unsigned int nTimeTx, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake)
|
|
387
|
+
{
|
|
388
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
389
|
+
const CBlockIndex* pindex = pindexPrev;
|
|
390
|
+
nStakeModifierHeight = pindex->nHeight;
|
|
391
|
+
nStakeModifierTime = pindex->GetBlockTime();
|
|
392
|
+
int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
|
|
393
|
+
|
|
394
|
+
if (nStakeModifierTime + params.nStakeMinAge - nStakeModifierSelectionInterval <= (int64_t) nTimeTx)
|
|
395
|
+
{
|
|
396
|
+
// Best block is still more than
|
|
397
|
+
// (nStakeMinAge minus a selection interval) older than kernel timestamp
|
|
398
|
+
if (fPrintProofOfStake)
|
|
399
|
+
return error("GetKernelStakeModifier() : best block %s at height %d too old for stake",
|
|
400
|
+
pindex->GetBlockHash().ToString(), pindex->nHeight);
|
|
401
|
+
else
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
// loop to find the stake modifier earlier by
|
|
405
|
+
// (nStakeMinAge minus a selection interval)
|
|
406
|
+
while (nStakeModifierTime + params.nStakeMinAge - nStakeModifierSelectionInterval >(int64_t) nTimeTx)
|
|
407
|
+
{
|
|
408
|
+
if (!pindex->pprev)
|
|
409
|
+
{ // reached genesis block; should not happen
|
|
410
|
+
return error("GetKernelStakeModifier() : reached genesis block");
|
|
411
|
+
}
|
|
412
|
+
pindex = pindex->pprev;
|
|
413
|
+
if (pindex->GeneratedStakeModifier())
|
|
414
|
+
{
|
|
415
|
+
nStakeModifierHeight = pindex->nHeight;
|
|
416
|
+
nStakeModifierTime = pindex->GetBlockTime();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
nStakeModifier = pindex->nStakeModifier;
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// V0.3: Stake modifier used to hash for a stake kernel is chosen as the stake
|
|
424
|
+
// modifier about a selection interval later than the coin generating the kernel
|
|
425
|
+
static bool GetKernelStakeModifierV03(CBlockIndex* pindexPrev, uint256 hashBlockFrom, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake, CChainState& chainstate)
|
|
426
|
+
{
|
|
427
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
428
|
+
nStakeModifier = 0;
|
|
429
|
+
|
|
430
|
+
const CBlockIndex* pindexFrom = chainstate.m_blockman.LookupBlockIndex(hashBlockFrom);
|
|
431
|
+
if (!pindexFrom)
|
|
432
|
+
return error("GetKernelStakeModifier() : block not indexed");
|
|
433
|
+
|
|
434
|
+
nStakeModifierHeight = pindexFrom->nHeight;
|
|
435
|
+
nStakeModifierTime = pindexFrom->GetBlockTime();
|
|
436
|
+
int64_t nStakeModifierSelectionInterval = GetStakeModifierSelectionInterval();
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
// we need to iterate index forward but we cannot depend on chainActive.Next()
|
|
440
|
+
// because there is no guarantee that we are checking blocks in active chain.
|
|
441
|
+
// So, we construct a temporary chain that we will iterate over.
|
|
442
|
+
// pindexFrom - this block contains coins that are used to generate PoS
|
|
443
|
+
// pindexPrev - this is a block that is previous to PoS block that we are checking, you can think of it as tip of our chain
|
|
444
|
+
std::vector<CBlockIndex*> tmpChain;
|
|
445
|
+
int32_t nDepth = pindexPrev->nHeight - (pindexFrom->nHeight-1); // -1 is used to also include pindexFrom
|
|
446
|
+
tmpChain.reserve(nDepth);
|
|
447
|
+
CBlockIndex* it = pindexPrev;
|
|
448
|
+
for (int i=1; i<=nDepth && !chainstate.m_chain.Contains(it); i++) {
|
|
449
|
+
tmpChain.push_back(it);
|
|
450
|
+
it = it->pprev;
|
|
451
|
+
}
|
|
452
|
+
std::reverse(tmpChain.begin(), tmpChain.end());
|
|
453
|
+
size_t n = 0;
|
|
454
|
+
|
|
455
|
+
const CBlockIndex* pindex = pindexFrom;
|
|
456
|
+
// loop to find the stake modifier later by a selection interval
|
|
457
|
+
while (nStakeModifierTime < pindexFrom->GetBlockTime() + nStakeModifierSelectionInterval)
|
|
458
|
+
{
|
|
459
|
+
const CBlockIndex* old_pindex = pindex;
|
|
460
|
+
pindex = (!tmpChain.empty() && pindex->nHeight >= tmpChain[0]->nHeight - 1)? tmpChain[n++] : chainstate.m_chain.Next(pindex);
|
|
461
|
+
if (n > tmpChain.size() || pindex == NULL) // check if tmpChain[n+1] exists
|
|
462
|
+
{ // reached best block; may happen if node is behind on block chain
|
|
463
|
+
if (fPrintProofOfStake || (old_pindex->GetBlockTime() + params.nStakeMinAge - nStakeModifierSelectionInterval > GetAdjustedTime()))
|
|
464
|
+
return error("GetKernelStakeModifier() : reached best block %s at height %d from block %s",
|
|
465
|
+
old_pindex->GetBlockHash().ToString(), old_pindex->nHeight, hashBlockFrom.ToString());
|
|
466
|
+
else
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
if (pindex->GeneratedStakeModifier())
|
|
470
|
+
{
|
|
471
|
+
nStakeModifierHeight = pindex->nHeight;
|
|
472
|
+
nStakeModifierTime = pindex->GetBlockTime();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
nStakeModifier = pindex->nStakeModifier;
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Get the stake modifier specified by the protocol to hash for a stake kernel
|
|
480
|
+
static bool GetKernelStakeModifier(CBlockIndex* pindexPrev, uint256 hashBlockFrom, unsigned int nTimeTx, uint64_t& nStakeModifier, int& nStakeModifierHeight, int64_t& nStakeModifierTime, bool fPrintProofOfStake, CChainState& chainstate)
|
|
481
|
+
{
|
|
482
|
+
if (IsProtocolV05(nTimeTx))
|
|
483
|
+
return GetKernelStakeModifierV05(pindexPrev, nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake);
|
|
484
|
+
else
|
|
485
|
+
return GetKernelStakeModifierV03(pindexPrev, hashBlockFrom, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake, chainstate);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// peercoin kernel protocol
|
|
489
|
+
// coinstake must meet hash target according to the protocol:
|
|
490
|
+
// kernel (input 0) must meet the formula
|
|
491
|
+
// hash(nStakeModifier + txPrev.block.nTime + txPrev.offset + txPrev.nTime + txPrev.vout.n + nTime) < bnTarget * nCoinDayWeight
|
|
492
|
+
// this ensures that the chance of getting a coinstake is proportional to the
|
|
493
|
+
// amount of coin age one owns.
|
|
494
|
+
// The reason this hash is chosen is the following:
|
|
495
|
+
// nStakeModifier:
|
|
496
|
+
// (v0.5) uses dynamic stake modifier around 21 days before the kernel,
|
|
497
|
+
// versus static stake modifier about 9 days after the staked
|
|
498
|
+
// coin (txPrev) used in v0.3
|
|
499
|
+
// (v0.3) scrambles computation to make it very difficult to precompute
|
|
500
|
+
// future proof-of-stake at the time of the coin's confirmation
|
|
501
|
+
// (v0.2) nBits (deprecated): encodes all past block timestamps
|
|
502
|
+
// txPrev.block.nTime: prevent nodes from guessing a good timestamp to
|
|
503
|
+
// generate transaction for future advantage
|
|
504
|
+
// txPrev.offset: offset of txPrev inside block, to reduce the chance of
|
|
505
|
+
// nodes generating coinstake at the same time
|
|
506
|
+
// txPrev.nTime: reduce the chance of nodes generating coinstake at the same
|
|
507
|
+
// time
|
|
508
|
+
// txPrev.vout.n: output number of txPrev, to reduce the chance of nodes
|
|
509
|
+
// generating coinstake at the same time
|
|
510
|
+
// block/tx hash should not be used here as they can be generated in vast
|
|
511
|
+
// quantities so as to generate blocks faster, degrading the system back into
|
|
512
|
+
// a proof-of-work situation.
|
|
513
|
+
//
|
|
514
|
+
bool CheckStakeKernelHash(unsigned int nBits, CBlockIndex* pindexPrev, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake, CChainState& chainstate)
|
|
515
|
+
{
|
|
516
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
517
|
+
unsigned int nTimeBlockFrom = blockFrom.GetBlockTime();
|
|
518
|
+
|
|
519
|
+
if (nTimeTx < (txPrev->nTime? txPrev->nTime : nTimeBlockFrom)) // Transaction timestamp violation
|
|
520
|
+
return error("CheckStakeKernelHash() : nTime violation");
|
|
521
|
+
|
|
522
|
+
if (nTimeBlockFrom + params.nStakeMinAge > nTimeTx) // Min age requirement
|
|
523
|
+
return error("CheckStakeKernelHash() : min age violation");
|
|
524
|
+
|
|
525
|
+
CBigNum bnTargetPerCoinDay;
|
|
526
|
+
bnTargetPerCoinDay.SetCompact(nBits);
|
|
527
|
+
int64_t nValueIn = txPrev->vout[prevout.n].nValue;
|
|
528
|
+
// v0.3 protocol kernel hash weight starts from 0 at the 30-day min age
|
|
529
|
+
// this change increases active coins participating the hash and helps
|
|
530
|
+
// to secure the network when proof-of-stake difficulty is low
|
|
531
|
+
int64_t nTimeWeight = min((int64_t)nTimeTx - (txPrev->nTime? txPrev->nTime : nTimeBlockFrom), params.nStakeMaxAge) - (IsProtocolV03(nTimeTx)? params.nStakeMinAge : 0);
|
|
532
|
+
CBigNum bnCoinDayWeight = CBigNum(nValueIn) * nTimeWeight / COIN / (24 * 60 * 60);
|
|
533
|
+
// Calculate hash
|
|
534
|
+
CDataStream ss(SER_GETHASH, 0);
|
|
535
|
+
uint64_t nStakeModifier = 0;
|
|
536
|
+
int nStakeModifierHeight = 0;
|
|
537
|
+
int64_t nStakeModifierTime = 0;
|
|
538
|
+
if (IsProtocolV03(nTimeTx)) // v0.3 protocol
|
|
539
|
+
{
|
|
540
|
+
if (!GetKernelStakeModifier(pindexPrev, blockFrom.GetHash(), nTimeTx, nStakeModifier, nStakeModifierHeight, nStakeModifierTime, fPrintProofOfStake, chainstate))
|
|
541
|
+
return false;
|
|
542
|
+
ss << nStakeModifier;
|
|
543
|
+
}
|
|
544
|
+
else // v0.2 protocol
|
|
545
|
+
{
|
|
546
|
+
ss << nBits;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
ss << nTimeBlockFrom << nTxPrevOffset << (txPrev->nTime? txPrev->nTime : nTimeBlockFrom) << prevout.n << nTimeTx;
|
|
550
|
+
hashProofOfStake = Hash(ss);
|
|
551
|
+
if (fPrintProofOfStake)
|
|
552
|
+
{
|
|
553
|
+
if (IsProtocolV03(nTimeTx)) {
|
|
554
|
+
const CBlockIndex* pindexTmp = chainstate.m_blockman.LookupBlockIndex(blockFrom.GetHash());
|
|
555
|
+
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
|
|
556
|
+
nStakeModifier, nStakeModifierHeight,
|
|
557
|
+
FormatISO8601DateTime(nStakeModifierTime),
|
|
558
|
+
pindexTmp->nHeight,
|
|
559
|
+
FormatISO8601DateTime(blockFrom.GetBlockTime()));
|
|
560
|
+
}
|
|
561
|
+
LogPrintf("CheckStakeKernelHash() : check protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
|
|
562
|
+
IsProtocolV05(nTimeTx)? "0.5" : (IsProtocolV03(nTimeTx)? "0.3" : "0.2"),
|
|
563
|
+
IsProtocolV03(nTimeTx)? nStakeModifier : (uint64_t) nBits,
|
|
564
|
+
nTimeBlockFrom, nTxPrevOffset, (txPrev->nTime? txPrev->nTime : nTimeBlockFrom), prevout.n, nTimeTx,
|
|
565
|
+
hashProofOfStake.ToString());
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Now check if proof-of-stake hash meets target protocol
|
|
569
|
+
if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
|
|
570
|
+
return false;
|
|
571
|
+
if (gArgs.GetBoolArg("-debug", false) && !fPrintProofOfStake)
|
|
572
|
+
{
|
|
573
|
+
if (IsProtocolV03(nTimeTx)) {
|
|
574
|
+
const CBlockIndex* pindexTmp = chainstate.m_blockman.LookupBlockIndex(blockFrom.GetHash());
|
|
575
|
+
LogPrintf("CheckStakeKernelHash() : using modifier 0x%016x at height=%d timestamp=%s for block from height=%d timestamp=%s\n",
|
|
576
|
+
nStakeModifier, nStakeModifierHeight,
|
|
577
|
+
FormatISO8601DateTime(nStakeModifierTime),
|
|
578
|
+
pindexTmp->nHeight,
|
|
579
|
+
FormatISO8601DateTime(blockFrom.GetBlockTime()));
|
|
580
|
+
}
|
|
581
|
+
LogPrintf("CheckStakeKernelHash() : pass protocol=%s modifier=0x%016x nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n",
|
|
582
|
+
IsProtocolV03(nTimeTx)? "0.3" : "0.2",
|
|
583
|
+
IsProtocolV03(nTimeTx)? nStakeModifier : (uint64_t) nBits,
|
|
584
|
+
nTimeBlockFrom, nTxPrevOffset, (txPrev->nTime? txPrev->nTime : nTimeBlockFrom), prevout.n, nTimeTx,
|
|
585
|
+
hashProofOfStake.ToString());
|
|
586
|
+
}
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Check kernel hash target and coinstake signature
|
|
591
|
+
bool CheckProofOfStake(BlockValidationState &state, CBlockIndex* pindexPrev, const CTransactionRef& tx, unsigned int nBits, uint256& hashProofOfStake, unsigned int nTimeTx, CChainState& chainstate)
|
|
592
|
+
{
|
|
593
|
+
if (!tx->IsCoinStake())
|
|
594
|
+
return error("CheckProofOfStake() : called on non-coinstake %s", tx->GetHash().ToString());
|
|
595
|
+
|
|
596
|
+
// Kernel (input 0) must match the stake hash target per coin age (nBits)
|
|
597
|
+
const CTxIn& txin = tx->vin[0];
|
|
598
|
+
|
|
599
|
+
// Transaction index is required to get to block header
|
|
600
|
+
if (!g_txindex)
|
|
601
|
+
return error("CheckProofOfStake() : transaction index not available");
|
|
602
|
+
|
|
603
|
+
// Get transaction index for the previous transaction
|
|
604
|
+
CDiskTxPos postx;
|
|
605
|
+
if (!g_txindex->FindTxPosition(txin.prevout.hash, postx))
|
|
606
|
+
return error("CheckProofOfStake() : tx index not found"); // tx index not found
|
|
607
|
+
|
|
608
|
+
// Read txPrev and header of its block
|
|
609
|
+
CBlockHeader header;
|
|
610
|
+
CTransactionRef txPrev;
|
|
611
|
+
auto it = g_txindex->cachedTxs.find(txin.prevout.hash);
|
|
612
|
+
if (it != g_txindex->cachedTxs.end()) {
|
|
613
|
+
header = it->second.first;
|
|
614
|
+
txPrev = it->second.second;
|
|
615
|
+
} else {
|
|
616
|
+
CAutoFile file(node::OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
|
617
|
+
try {
|
|
618
|
+
file >> header;
|
|
619
|
+
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
|
620
|
+
file >> txPrev;
|
|
621
|
+
} catch (std::exception &e) {
|
|
622
|
+
return error("%s() : deserialize or I/O error in CheckProofOfStake()", __PRETTY_FUNCTION__);
|
|
623
|
+
}
|
|
624
|
+
//g_txindex->cachedTxs[txin.prevout.hash] = std::pair(header,txPrev);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (txPrev->GetHash() != txin.prevout.hash)
|
|
628
|
+
return error("%s() : txid mismatch in CheckProofOfStake()", __PRETTY_FUNCTION__);
|
|
629
|
+
|
|
630
|
+
// Verify signature
|
|
631
|
+
{
|
|
632
|
+
int nIn = 0;
|
|
633
|
+
const CTxOut& prevOut = txPrev->vout[tx->vin[nIn].prevout.n];
|
|
634
|
+
TransactionSignatureChecker checker(&(*tx), nIn, prevOut.nValue, PrecomputedTransactionData(*tx), MissingDataBehavior(1));
|
|
635
|
+
|
|
636
|
+
if (!VerifyScript(tx->vin[nIn].scriptSig, prevOut.scriptPubKey, &(tx->vin[nIn].scriptWitness), SCRIPT_VERIFY_P2SH, checker, nullptr))
|
|
637
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "invalid-pos-script", strprintf("%s: VerifyScript failed on coinstake %s", __func__, tx->GetHash().ToString()));
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (!CheckStakeKernelHash(nBits, pindexPrev, header, postx.nTxOffset + CBlockHeader::NORMAL_SERIALIZE_SIZE, txPrev, txin.prevout, nTimeTx, hashProofOfStake, gArgs.GetBoolArg("-debug", false), chainstate))
|
|
641
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "check-kernel-failed", strprintf("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx->GetHash().ToString(), hashProofOfStake.ToString())); // may occur during initial download or if behind on block chain sync
|
|
642
|
+
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Check whether the coinstake timestamp meets protocol
|
|
647
|
+
bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx)
|
|
648
|
+
{
|
|
649
|
+
if (IsProtocolV03(nTimeTx)) // v0.3 protocol
|
|
650
|
+
return (nTimeBlock == nTimeTx);
|
|
651
|
+
else // v0.2 protocol
|
|
652
|
+
return ((nTimeTx <= nTimeBlock) && (nTimeBlock <= nTimeTx + MAX_FUTURE_BLOCK_TIME_PREV9));
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Get stake modifier checksum
|
|
656
|
+
unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex)
|
|
657
|
+
{
|
|
658
|
+
assert (pindex->pprev || pindex->GetBlockHash() == Params().GetConsensus().hashGenesisBlock);
|
|
659
|
+
// Hash previous checksum with flags, hashProofOfStake and nStakeModifier
|
|
660
|
+
CDataStream ss(SER_GETHASH, 0);
|
|
661
|
+
if (pindex->pprev)
|
|
662
|
+
ss << pindex->pprev->nStakeModifierChecksum;
|
|
663
|
+
ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier;
|
|
664
|
+
arith_uint256 hashChecksum = UintToArith256(Hash(ss));
|
|
665
|
+
hashChecksum >>= (256 - 32);
|
|
666
|
+
return hashChecksum.GetLow64();
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Check stake modifier hard checkpoints
|
|
670
|
+
bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum)
|
|
671
|
+
{
|
|
672
|
+
bool fTestNet = Params().NetworkIDString() == CBaseChainParams::TESTNET;
|
|
673
|
+
if (fTestNet && mapStakeModifierTestnetCheckpoints.count(nHeight))
|
|
674
|
+
return nStakeModifierChecksum == mapStakeModifierTestnetCheckpoints[nHeight];
|
|
675
|
+
|
|
676
|
+
if (!fTestNet && mapStakeModifierCheckpoints.count(nHeight))
|
|
677
|
+
return nStakeModifierChecksum == mapStakeModifierCheckpoints[nHeight];
|
|
678
|
+
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
|
|
683
|
+
{
|
|
684
|
+
return (HowSuperMajority(minVersion, pstart, nRequired, nToCheck) >= nRequired);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
unsigned int HowSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck)
|
|
688
|
+
{
|
|
689
|
+
unsigned int nFound = 0;
|
|
690
|
+
for (unsigned int i = 0; i < nToCheck && nFound < nRequired && pstart != NULL; pstart = pstart->pprev )
|
|
691
|
+
{
|
|
692
|
+
if (!pstart->IsProofOfStake())
|
|
693
|
+
continue;
|
|
694
|
+
|
|
695
|
+
if (pstart->nVersion >= minVersion)
|
|
696
|
+
++nFound;
|
|
697
|
+
|
|
698
|
+
i++;
|
|
699
|
+
}
|
|
700
|
+
return nFound;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// peercoin: entropy bit for stake modifier if chosen by modifier
|
|
704
|
+
unsigned int GetStakeEntropyBit(const CBlock& block)
|
|
705
|
+
{
|
|
706
|
+
unsigned int nEntropyBit = 0;
|
|
707
|
+
if (IsProtocolV04(block.nTime))
|
|
708
|
+
{
|
|
709
|
+
nEntropyBit = UintToArith256(block.GetHash()).GetLow64() & 1llu;// last bit of block hash
|
|
710
|
+
if (gArgs.GetBoolArg("-printstakemodifier", false))
|
|
711
|
+
LogPrintf("GetStakeEntropyBit(v0.4+): nTime=%u hashBlock=%s entropybit=%d\n", block.nTime, block.GetHash().ToString(), nEntropyBit);
|
|
712
|
+
}
|
|
713
|
+
else
|
|
714
|
+
{
|
|
715
|
+
// old protocol for entropy bit pre v0.4
|
|
716
|
+
uint160 hashSig = Hash160(block.vchBlockSig);
|
|
717
|
+
if (gArgs.GetBoolArg("-printstakemodifier", false))
|
|
718
|
+
LogPrintf("GetStakeEntropyBit(v0.3): nTime=%u hashSig=%s", block.nTime, hashSig.ToString());
|
|
719
|
+
nEntropyBit = hashSig.GetDataPtr()[4] >> 31; // take the first bit of the hash
|
|
720
|
+
if (gArgs.GetBoolArg("-printstakemodifier", false))
|
|
721
|
+
LogPrintf(" entropybit=%d\n", nEntropyBit);
|
|
722
|
+
}
|
|
723
|
+
return nEntropyBit;
|
|
724
|
+
}
|
|
725
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Copyright (c) 2012-2023 The Peercoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#ifndef PEERCOIN_KERNEL_H
|
|
5
|
+
#define PEERCOIN_KERNEL_H
|
|
6
|
+
|
|
7
|
+
#include <primitives/transaction.h> // CTransaction(Ref)
|
|
8
|
+
|
|
9
|
+
class CBlockIndex;
|
|
10
|
+
class BlockValidationState;
|
|
11
|
+
class CBlockHeader;
|
|
12
|
+
class CBlock;
|
|
13
|
+
class CChainState;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// MODIFIER_INTERVAL_RATIO:
|
|
17
|
+
// ratio of group interval length between the last group and the first group
|
|
18
|
+
static const int MODIFIER_INTERVAL_RATIO = 3;
|
|
19
|
+
|
|
20
|
+
// Protocol switch time of v0.3 kernel protocol
|
|
21
|
+
extern unsigned int nProtocolV03SwitchTime;
|
|
22
|
+
extern unsigned int nProtocolV03TestSwitchTime;
|
|
23
|
+
|
|
24
|
+
// Whether a given coinstake is subject to new v0.3 protocol
|
|
25
|
+
bool IsProtocolV03(unsigned int nTimeCoinStake);
|
|
26
|
+
// Whether a given block is subject to new v0.4 protocol
|
|
27
|
+
bool IsProtocolV04(unsigned int nTimeBlock);
|
|
28
|
+
// Whether a given transaction is subject to new v0.5 protocol
|
|
29
|
+
bool IsProtocolV05(unsigned int nTimeTx);
|
|
30
|
+
// Whether a given block is subject to new v0.6 protocol
|
|
31
|
+
// Test against previous block index! (always available)
|
|
32
|
+
bool IsProtocolV06(const CBlockIndex *pindexPrev);
|
|
33
|
+
// Whether a given transaction is subject to new v0.7 protocol
|
|
34
|
+
bool IsProtocolV07(unsigned int nTimeTx);
|
|
35
|
+
// Whether a given block is subject to new BIPs from bitcoin 0.16.x
|
|
36
|
+
bool IsBTC16BIPsEnabled(uint32_t nTimeTx);
|
|
37
|
+
// Whether a given timestamp is subject to new v0.9 protocol
|
|
38
|
+
bool IsProtocolV09(unsigned int nTimeTx);
|
|
39
|
+
// Whether a given timestamp is subject to new v10 protocol
|
|
40
|
+
bool IsProtocolV10(unsigned int nTimeTx);
|
|
41
|
+
// Whether a given block is subject to new v12 protocol
|
|
42
|
+
bool IsProtocolV12(const CBlockIndex* pindexPrev);
|
|
43
|
+
|
|
44
|
+
// Compute the hash modifier for proof-of-stake
|
|
45
|
+
bool ComputeNextStakeModifier(const CBlockIndex* pindexCurrent, uint64_t& nStakeModifier, bool& fGeneratedStakeModifier, CChainState& chainstate);
|
|
46
|
+
|
|
47
|
+
// Check whether stake kernel meets hash target
|
|
48
|
+
// Sets hashProofOfStake on success return
|
|
49
|
+
bool CheckStakeKernelHash(unsigned int nBits, CBlockIndex* pindexPrev, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, const CTransactionRef& txPrev, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, bool fPrintProofOfStake, CChainState& chainstate);
|
|
50
|
+
|
|
51
|
+
// Check kernel hash target and coinstake signature
|
|
52
|
+
// Sets hashProofOfStake on success return
|
|
53
|
+
bool CheckProofOfStake(BlockValidationState &state, CBlockIndex* pindexPrev, const CTransactionRef &tx, unsigned int nBits, uint256& hashProofOfStake, unsigned int nTimeTx, CChainState& chainstate);
|
|
54
|
+
|
|
55
|
+
// Check whether the coinstake timestamp meets protocol
|
|
56
|
+
bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx);
|
|
57
|
+
|
|
58
|
+
// Get stake modifier checksum
|
|
59
|
+
unsigned int GetStakeModifierChecksum(const CBlockIndex* pindex);
|
|
60
|
+
|
|
61
|
+
// Check stake modifier hard checkpoints
|
|
62
|
+
bool CheckStakeModifierCheckpoints(int nHeight, unsigned int nStakeModifierChecksum);
|
|
63
|
+
|
|
64
|
+
// peercoin: block version supermajority calculation
|
|
65
|
+
bool IsSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck);
|
|
66
|
+
unsigned int HowSuperMajority(int minVersion, const CBlockIndex* pstart, unsigned int nRequired, unsigned int nToCheck);
|
|
67
|
+
|
|
68
|
+
// peercoin: entropy bit for stake modifier if chosen by modifier
|
|
69
|
+
unsigned int GetStakeEntropyBit(const CBlock& block);
|
|
70
|
+
|
|
71
|
+
#endif // PEERCOIN_KERNEL_H
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#include <kernelrecord.h>
|
|
2
|
+
#include <key_io.h>
|
|
3
|
+
#include <wallet/wallet.h>
|
|
4
|
+
#include <base58.h>
|
|
5
|
+
#include <chainparams.h>
|
|
6
|
+
#include <timedata.h>
|
|
7
|
+
#include <interfaces/wallet.h>
|
|
8
|
+
#include <math.h>
|
|
9
|
+
using namespace std;
|
|
10
|
+
|
|
11
|
+
bool KernelRecord::showTransaction(bool isCoinbase, int depth)
|
|
12
|
+
{
|
|
13
|
+
if (isCoinbase) {
|
|
14
|
+
if (depth < 2)
|
|
15
|
+
return false;
|
|
16
|
+
} else {
|
|
17
|
+
if (depth == 0)
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/*
|
|
25
|
+
* Decompose CWallet transaction to model kernel records.
|
|
26
|
+
*/
|
|
27
|
+
vector<KernelRecord> KernelRecord::decomposeOutput(interfaces::Wallet& wallet, const interfaces::WalletTx &wtx)
|
|
28
|
+
{
|
|
29
|
+
vector<KernelRecord> parts;
|
|
30
|
+
int64_t nTime = (wtx.tx->nTime ? wtx.tx->nTime : wtx.time);
|
|
31
|
+
uint256 hash = wtx.tx->GetHash();
|
|
32
|
+
std::map<std::string, std::string> mapValue = wtx.value_map;
|
|
33
|
+
|
|
34
|
+
int numBlocks;
|
|
35
|
+
interfaces::WalletTxStatus status;
|
|
36
|
+
interfaces::WalletOrderForm orderForm;
|
|
37
|
+
bool inMempool;
|
|
38
|
+
wallet.getWalletTxDetails(hash, status, orderForm, inMempool, numBlocks);
|
|
39
|
+
|
|
40
|
+
if (showTransaction(wtx.is_coinbase, status.depth_in_main_chain)) {
|
|
41
|
+
for (size_t nOut = 0; nOut < wtx.tx->vout.size(); nOut++) {
|
|
42
|
+
CTxOut txOut = wtx.tx->vout[nOut];
|
|
43
|
+
if (wallet.txoutIsMine(txOut)) {
|
|
44
|
+
CTxDestination address;
|
|
45
|
+
std::string addrStr;
|
|
46
|
+
|
|
47
|
+
if (ExtractDestination(txOut.scriptPubKey, address)) {
|
|
48
|
+
// Sent to Bitcoin Address
|
|
49
|
+
addrStr = EncodeDestination(address);
|
|
50
|
+
} else {
|
|
51
|
+
// Sent to IP, or other non-address transaction like OP_EVAL
|
|
52
|
+
addrStr = mapValue["to"];
|
|
53
|
+
}
|
|
54
|
+
std::vector<interfaces::WalletTxOut> coins = wallet.getCoins({COutPoint(hash, nOut)});
|
|
55
|
+
bool isSpent = coins.size() >= 1 ? coins[0].is_spent : true;
|
|
56
|
+
parts.push_back(KernelRecord(hash, nTime, addrStr, txOut.nValue, nOut, isSpent));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return parts;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
std::string KernelRecord::getTxID()
|
|
65
|
+
{
|
|
66
|
+
return hash.ToString() + strprintf("-%03d", idx);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
int64_t KernelRecord::getAge() const
|
|
70
|
+
{
|
|
71
|
+
return (GetAdjustedTime() - nTime) / 86400;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
int64_t KernelRecord::getCoinAge() const
|
|
75
|
+
{
|
|
76
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
77
|
+
int nDayWeight = (min((GetAdjustedTime() - nTime), params.nStakeMaxAge) - params.nStakeMinAge) / 86400;
|
|
78
|
+
return max(nValue * nDayWeight / COIN, (int64_t) 0);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
double KernelRecord::getProbToMintStake(double difficulty, int timeOffset) const
|
|
82
|
+
{
|
|
83
|
+
const Consensus::Params& params = Params().GetConsensus();
|
|
84
|
+
double maxTarget = pow(static_cast<double>(2), 224);
|
|
85
|
+
double target = maxTarget / difficulty;
|
|
86
|
+
int dayWeight = (min((GetAdjustedTime() - nTime) + timeOffset, params.nStakeMaxAge) - params.nStakeMinAge) / 86400;
|
|
87
|
+
uint64_t coinAge = max(nValue * dayWeight / COIN, (int64_t)0);
|
|
88
|
+
return target * coinAge / pow(static_cast<double>(2), 256);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
double KernelRecord::getProbToMintWithinNMinutes(double difficulty, int minutes)
|
|
92
|
+
{
|
|
93
|
+
if(difficulty != prevDifficulty || minutes != prevMinutes)
|
|
94
|
+
{
|
|
95
|
+
double prob = 1;
|
|
96
|
+
double p;
|
|
97
|
+
int d = minutes / (60 * 24); // Number of full days
|
|
98
|
+
int m = minutes % (60 * 24); // Number of minutes in the last day
|
|
99
|
+
int i, timeOffset;
|
|
100
|
+
|
|
101
|
+
// Probabilities for the first d days
|
|
102
|
+
for(i = 0; i < d; i++)
|
|
103
|
+
{
|
|
104
|
+
timeOffset = i * 86400;
|
|
105
|
+
p = pow(1 - getProbToMintStake(difficulty, timeOffset), 86400);
|
|
106
|
+
prob *= p;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Probability for the m minutes of the last day
|
|
110
|
+
timeOffset = d * 86400;
|
|
111
|
+
p = pow(1 - getProbToMintStake(difficulty, timeOffset), 60 * m);
|
|
112
|
+
prob *= p;
|
|
113
|
+
|
|
114
|
+
prob = 1 - prob;
|
|
115
|
+
prevProbability = prob;
|
|
116
|
+
prevDifficulty = difficulty;
|
|
117
|
+
prevMinutes = minutes;
|
|
118
|
+
}
|
|
119
|
+
return prevProbability;
|
|
120
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Copyright (c) 2012-2023 The Peercoin developers
|
|
2
|
+
// Distributed under the MIT software license, see the accompanying
|
|
3
|
+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
+
#ifndef PEERCOIN_KERNELRECORD_H
|
|
5
|
+
#define PEERCOIN_KERNELRECORD_H
|
|
6
|
+
|
|
7
|
+
#include <uint256.h>
|
|
8
|
+
#include <interfaces/wallet.h>
|
|
9
|
+
|
|
10
|
+
namespace wallet {
|
|
11
|
+
class CWallet;
|
|
12
|
+
} // namespace wallet
|
|
13
|
+
//class CWallet;
|
|
14
|
+
using wallet::CWallet;
|
|
15
|
+
class CWalletTx;
|
|
16
|
+
|
|
17
|
+
class KernelRecord
|
|
18
|
+
{
|
|
19
|
+
public:
|
|
20
|
+
KernelRecord():
|
|
21
|
+
hash(), nTime(0), address(""), nValue(0), idx(0), spent(false), prevMinutes(0), prevDifficulty(0), prevProbability(0)
|
|
22
|
+
{
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
KernelRecord(uint256 hash, int64_t nTime):
|
|
26
|
+
hash(hash), nTime(nTime), address(""), nValue(0), idx(0), spent(false), prevMinutes(0), prevDifficulty(0), prevProbability(0)
|
|
27
|
+
{
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
KernelRecord(uint256 hash, int64_t nTime,
|
|
31
|
+
const std::string &address,
|
|
32
|
+
int64_t nValue, int idx, bool spent):
|
|
33
|
+
hash(hash), nTime(nTime), address(address), nValue(nValue),
|
|
34
|
+
idx(idx), spent(spent), prevMinutes(0), prevDifficulty(0), prevProbability(0)
|
|
35
|
+
{
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static bool showTransaction(bool isCoinbase, int depth);
|
|
39
|
+
static std::vector<KernelRecord> decomposeOutput(interfaces::Wallet &wallet, const interfaces::WalletTx &wtx);
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
uint256 hash;
|
|
43
|
+
int64_t nTime;
|
|
44
|
+
std::string address;
|
|
45
|
+
int64_t nValue;
|
|
46
|
+
int idx;
|
|
47
|
+
bool spent;
|
|
48
|
+
|
|
49
|
+
std::string getTxID();
|
|
50
|
+
int64_t getAge() const;
|
|
51
|
+
int64_t getCoinAge() const;
|
|
52
|
+
double getProbToMintStake(double difficulty, int timeOffset = 0) const;
|
|
53
|
+
double getProbToMintWithinNMinutes(double difficulty, int minutes);
|
|
54
|
+
protected:
|
|
55
|
+
int prevMinutes;
|
|
56
|
+
double prevDifficulty;
|
|
57
|
+
double prevProbability;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
#endif // PEERCOIN_KERNELRECORD_H
|
|
@@ -118,6 +118,10 @@ std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mute
|
|
|
118
118
|
static bool vfLimited[NET_MAX] GUARDED_BY(g_maplocalhost_mutex) = {};
|
|
119
119
|
std::string strSubVersion;
|
|
120
120
|
|
|
121
|
+
// peercoin: temperature to measure how many PoS headers have been sent by this client
|
|
122
|
+
std::map<CNetAddr, int32_t> mapPoSTemperature;
|
|
123
|
+
std::set<std::pair<COutPoint, unsigned int>> setStakeSeen;
|
|
124
|
+
|
|
121
125
|
void CConnman::AddAddrFetch(const std::string& strDest)
|
|
122
126
|
{
|
|
123
127
|
LOCK(m_addr_fetches_mutex);
|
|
@@ -3031,6 +3035,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> s
|
|
|
3031
3035
|
nLocalServices(nLocalServicesIn)
|
|
3032
3036
|
{
|
|
3033
3037
|
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
|
|
3038
|
+
lastAcceptedHeader = uint256();
|
|
3034
3039
|
if (conn_type_in != ConnectionType::BLOCK_RELAY) {
|
|
3035
3040
|
m_tx_relay = std::make_unique<TxRelay>();
|
|
3036
3041
|
}
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
#include <net_permissions.h>
|
|
18
18
|
#include <netaddress.h>
|
|
19
19
|
#include <netbase.h>
|
|
20
|
-
#include <policy/feerate.h>
|
|
21
20
|
#include <protocol.h>
|
|
22
21
|
#include <random.h>
|
|
23
22
|
#include <span.h>
|
|
@@ -88,6 +87,10 @@ static constexpr bool DEFAULT_FIXEDSEEDS{true};
|
|
|
88
87
|
static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000;
|
|
89
88
|
static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000;
|
|
90
89
|
|
|
90
|
+
/** peercoin: Number of consecutive PoS headers are allowed from a single peer. Used to prevent out of memory attack. */
|
|
91
|
+
static const int32_t MAX_CONSECUTIVE_POS_HEADERS = 1000;
|
|
92
|
+
|
|
93
|
+
// const unsigned int POW_HEADER_COOLING = 70; - defined in protocol.cpp, so that it is visible to other files
|
|
91
94
|
typedef int64_t NodeId;
|
|
92
95
|
|
|
93
96
|
struct AddedNodeInfo
|
|
@@ -243,6 +246,8 @@ struct LocalServiceInfo {
|
|
|
243
246
|
|
|
244
247
|
extern Mutex g_maplocalhost_mutex;
|
|
245
248
|
extern std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex);
|
|
249
|
+
extern std::map<CNetAddr, int32_t> mapPoSTemperature;
|
|
250
|
+
extern std::set<std::pair<COutPoint, unsigned int>> setStakeSeen;
|
|
246
251
|
|
|
247
252
|
extern const std::string NET_MESSAGE_COMMAND_OTHER;
|
|
248
253
|
typedef std::map<std::string, uint64_t> mapMsgCmdSize; //command, total bytes
|
|
@@ -598,6 +603,8 @@ public:
|
|
|
598
603
|
/** Lowest measured round-trip time. Used as an inbound peer eviction
|
|
599
604
|
* criterium in CConnman::AttemptToEvictConnection. */
|
|
600
605
|
std::atomic<std::chrono::microseconds> m_min_ping_time{std::chrono::microseconds::max()};
|
|
606
|
+
// peercoin: used to detect branch switches
|
|
607
|
+
uint256 lastAcceptedHeader;
|
|
601
608
|
|
|
602
609
|
CNode(NodeId id, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> sock, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion);
|
|
603
610
|
CNode(const CNode&) = delete;
|
|
@@ -959,6 +966,14 @@ public:
|
|
|
959
966
|
/** Return true if we should disconnect the peer for failing an inactivity check. */
|
|
960
967
|
bool ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const;
|
|
961
968
|
|
|
969
|
+
/**
|
|
970
|
+
* This is signaled when network activity should cease.
|
|
971
|
+
* A pointer to it is saved in `m_i2p_sam_session`, so make sure that
|
|
972
|
+
* the lifetime of `interruptNet` is not shorter than
|
|
973
|
+
* the lifetime of `m_i2p_sam_session`.
|
|
974
|
+
*/
|
|
975
|
+
CThreadInterrupt interruptNet;
|
|
976
|
+
|
|
962
977
|
private:
|
|
963
978
|
struct ListenSocket {
|
|
964
979
|
public:
|
|
@@ -1203,14 +1218,6 @@ private:
|
|
|
1203
1218
|
Mutex mutexMsgProc;
|
|
1204
1219
|
std::atomic<bool> flagInterruptMsgProc{false};
|
|
1205
1220
|
|
|
1206
|
-
/**
|
|
1207
|
-
* This is signaled when network activity should cease.
|
|
1208
|
-
* A pointer to it is saved in `m_i2p_sam_session`, so make sure that
|
|
1209
|
-
* the lifetime of `interruptNet` is not shorter than
|
|
1210
|
-
* the lifetime of `m_i2p_sam_session`.
|
|
1211
|
-
*/
|
|
1212
|
-
CThreadInterrupt interruptNet;
|
|
1213
|
-
|
|
1214
1221
|
/**
|
|
1215
1222
|
* I2P SAM session.
|
|
1216
1223
|
* Used to accept incoming and make outgoing I2P connections.
|
|
@@ -9,17 +9,16 @@
|
|
|
9
9
|
#include <banman.h>
|
|
10
10
|
#include <blockencodings.h>
|
|
11
11
|
#include <blockfilter.h>
|
|
12
|
+
#include <chain.h>
|
|
12
13
|
#include <chainparams.h>
|
|
13
14
|
#include <consensus/amount.h>
|
|
14
15
|
#include <consensus/validation.h>
|
|
15
|
-
#include <deploymentstatus.h>
|
|
16
16
|
#include <hash.h>
|
|
17
17
|
#include <index/blockfilterindex.h>
|
|
18
18
|
#include <merkleblock.h>
|
|
19
19
|
#include <netbase.h>
|
|
20
20
|
#include <netmessagemaker.h>
|
|
21
21
|
#include <node/blockstorage.h>
|
|
22
|
-
#include <policy/fees.h>
|
|
23
22
|
#include <policy/policy.h>
|
|
24
23
|
#include <primitives/block.h>
|
|
25
24
|
#include <primitives/transaction.h>
|
|
@@ -45,10 +44,11 @@
|
|
|
45
44
|
#include <optional>
|
|
46
45
|
#include <typeinfo>
|
|
47
46
|
|
|
47
|
+
#include <kernel.h>
|
|
48
|
+
|
|
48
49
|
using node::ReadBlockFromDisk;
|
|
49
50
|
using node::ReadRawBlockFromDisk;
|
|
50
51
|
using node::fImporting;
|
|
51
|
-
using node::fPruneMode;
|
|
52
52
|
using node::fReindex;
|
|
53
53
|
|
|
54
54
|
/** How long to cache transactions in mapRelay for normal relay */
|
|
@@ -415,7 +415,7 @@ private:
|
|
|
415
415
|
void RelayAddress(NodeId originator, const CAddress& addr, bool fReachable);
|
|
416
416
|
|
|
417
417
|
/** Send `feefilter` message. */
|
|
418
|
-
void MaybeSendFeefilter(CNode& node, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
418
|
+
// void MaybeSendFeefilter(CNode& node, std::chrono::microseconds current_time) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
419
419
|
|
|
420
420
|
const CChainParams& m_chainparams;
|
|
421
421
|
CConnman& m_connman;
|
|
@@ -594,6 +594,13 @@ private:
|
|
|
594
594
|
TxOrphanage m_orphanage;
|
|
595
595
|
|
|
596
596
|
void AddToCompactExtraTransactions(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(g_cs_orphans);
|
|
597
|
+
/** peercoin: blocks that are waiting to be processed, the key points to previous CBlockIndex entry */
|
|
598
|
+
struct WaitElement {
|
|
599
|
+
std::shared_ptr<CBlock> pblock;
|
|
600
|
+
int64_t time;
|
|
601
|
+
};
|
|
602
|
+
std::map<CBlockIndex*, WaitElement> mapBlocksWait;
|
|
603
|
+
|
|
597
604
|
|
|
598
605
|
/** Orphan/conflicted/etc transactions that are kept for compact block reconstruction.
|
|
599
606
|
* The last -blockreconstructionextratxn/DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN of
|
|
@@ -1004,8 +1011,8 @@ void PeerManagerImpl::ProcessBlockAvailability(NodeId nodeid) {
|
|
|
1004
1011
|
|
|
1005
1012
|
if (!state->hashLastUnknownBlock.IsNull()) {
|
|
1006
1013
|
const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock);
|
|
1007
|
-
if (pindex && pindex->
|
|
1008
|
-
if (state->pindexBestKnownBlock == nullptr || pindex->
|
|
1014
|
+
if (pindex && pindex->nChainTrust > 0) {
|
|
1015
|
+
if (state->pindexBestKnownBlock == nullptr || pindex->nChainTrust >= state->pindexBestKnownBlock->nChainTrust) {
|
|
1009
1016
|
state->pindexBestKnownBlock = pindex;
|
|
1010
1017
|
}
|
|
1011
1018
|
state->hashLastUnknownBlock.SetNull();
|
|
@@ -1020,9 +1027,9 @@ void PeerManagerImpl::UpdateBlockAvailability(NodeId nodeid, const uint256 &hash
|
|
|
1020
1027
|
ProcessBlockAvailability(nodeid);
|
|
1021
1028
|
|
|
1022
1029
|
const CBlockIndex* pindex = m_chainman.m_blockman.LookupBlockIndex(hash);
|
|
1023
|
-
if (pindex && pindex->
|
|
1030
|
+
if (pindex && pindex->nChainTrust > 0) {
|
|
1024
1031
|
// An actually better block was announced.
|
|
1025
|
-
if (state->pindexBestKnownBlock == nullptr || pindex->
|
|
1032
|
+
if (state->pindexBestKnownBlock == nullptr || pindex->nChainTrust >= state->pindexBestKnownBlock->nChainTrust) {
|
|
1026
1033
|
state->pindexBestKnownBlock = pindex;
|
|
1027
1034
|
}
|
|
1028
1035
|
} else {
|
|
@@ -1043,7 +1050,7 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
|
|
|
1043
1050
|
// Make sure pindexBestKnownBlock is up to date, we'll need it.
|
|
1044
1051
|
ProcessBlockAvailability(nodeid);
|
|
1045
1052
|
|
|
1046
|
-
if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->
|
|
1053
|
+
if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainTrust < m_chainman.ActiveChain().Tip()->nChainTrust || state->pindexBestKnownBlock->nChainTrust < nMinimumChainWork) {
|
|
1047
1054
|
// This peer has nothing interesting.
|
|
1048
1055
|
return;
|
|
1049
1056
|
}
|
|
@@ -1060,7 +1067,6 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
|
|
|
1060
1067
|
if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
|
|
1061
1068
|
return;
|
|
1062
1069
|
|
|
1063
|
-
const Consensus::Params& consensusParams = m_chainparams.GetConsensus();
|
|
1064
1070
|
std::vector<const CBlockIndex*> vToFetch;
|
|
1065
1071
|
const CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
|
|
1066
1072
|
// Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
|
|
@@ -1084,13 +1090,13 @@ void PeerManagerImpl::FindNextBlocksToDownload(NodeId nodeid, unsigned int count
|
|
|
1084
1090
|
// Iterate over those blocks in vToFetch (in forward direction), adding the ones that
|
|
1085
1091
|
// are not yet downloaded and not in flight to vBlocks. In the meantime, update
|
|
1086
1092
|
// pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's
|
|
1087
|
-
// already part of our chain
|
|
1093
|
+
// already part of our chain.
|
|
1088
1094
|
for (const CBlockIndex* pindex : vToFetch) {
|
|
1089
1095
|
if (!pindex->IsValid(BLOCK_VALID_TREE)) {
|
|
1090
1096
|
// We consider the chain that this peer is on invalid.
|
|
1091
1097
|
return;
|
|
1092
1098
|
}
|
|
1093
|
-
if (!State(nodeid)->fHaveWitness &&
|
|
1099
|
+
if (!State(nodeid)->fHaveWitness && IsBTC16BIPsEnabled(pindex->nTime)) {
|
|
1094
1100
|
// We wouldn't download this block or its descendants from this peer.
|
|
1095
1101
|
return;
|
|
1096
1102
|
}
|
|
@@ -1594,7 +1600,7 @@ void PeerManagerImpl::NewPoWValidBlock(const CBlockIndex *pindex, const std::sha
|
|
|
1594
1600
|
return;
|
|
1595
1601
|
nHighestFastAnnounce = pindex->nHeight;
|
|
1596
1602
|
|
|
1597
|
-
bool fWitnessEnabled =
|
|
1603
|
+
bool fWitnessEnabled = IsBTC16BIPsEnabled(pindex->nTime);
|
|
1598
1604
|
uint256 hashBlock(pblock->GetHash());
|
|
1599
1605
|
|
|
1600
1606
|
{
|
|
@@ -1870,15 +1876,14 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
|
|
1870
1876
|
pfrom.fDisconnect = true;
|
|
1871
1877
|
return;
|
|
1872
1878
|
}
|
|
1873
|
-
//
|
|
1874
|
-
// it's available before trying to send.
|
|
1879
|
+
// Check whether the block is available before trying to send.
|
|
1875
1880
|
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) {
|
|
1876
1881
|
return;
|
|
1877
1882
|
}
|
|
1878
1883
|
std::shared_ptr<const CBlock> pblock;
|
|
1879
1884
|
if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) {
|
|
1880
1885
|
pblock = a_recent_block;
|
|
1881
|
-
} else if (inv.IsMsgWitnessBlk()) {
|
|
1886
|
+
} /* else if (inv.IsMsgWitnessBlk()) {
|
|
1882
1887
|
// Fast-path: in this case it is possible to serve the block directly from disk,
|
|
1883
1888
|
// as the network format matches the format on disk
|
|
1884
1889
|
std::vector<uint8_t> block_data;
|
|
@@ -1887,7 +1892,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
|
|
1887
1892
|
}
|
|
1888
1893
|
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::BLOCK, Span{block_data}));
|
|
1889
1894
|
// Don't set pblock as we've sent the block
|
|
1890
|
-
} else {
|
|
1895
|
+
} */ else {
|
|
1891
1896
|
// Send block from disk
|
|
1892
1897
|
std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>();
|
|
1893
1898
|
if (!ReadBlockFromDisk(*pblockRead, pindex, m_chainparams.GetConsensus())) {
|
|
@@ -1951,8 +1956,11 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv&
|
|
|
1951
1956
|
// Send immediately. This must send even if redundant,
|
|
1952
1957
|
// and we want it right after the last block so they don't
|
|
1953
1958
|
// wait for other stuff first.
|
|
1959
|
+
// peercoin: send latest proof-of-work block to allow the
|
|
1960
|
+
// download node to accept as orphan (proof-of-stake
|
|
1961
|
+
// block might be rejected by stake connection check)
|
|
1954
1962
|
std::vector<CInv> vInv;
|
|
1955
|
-
vInv.push_back(CInv(MSG_BLOCK, m_chainman.ActiveChain().Tip()->GetBlockHash()));
|
|
1963
|
+
vInv.push_back(CInv(MSG_BLOCK, GetLastBlockIndex(m_chainman.ActiveChain().Tip(), false)->GetBlockHash()));
|
|
1956
1964
|
m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::INV, vInv));
|
|
1957
1965
|
peer.m_continuation_block.SetNull();
|
|
1958
1966
|
}
|
|
@@ -2164,13 +2172,15 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2164
2172
|
}
|
|
2165
2173
|
}
|
|
2166
2174
|
|
|
2175
|
+
int32_t& nPoSTemperature = mapPoSTemperature[pfrom.addr];
|
|
2167
2176
|
BlockValidationState state;
|
|
2168
|
-
if (!m_chainman.ProcessNewBlockHeaders(headers, state, m_chainparams, &pindexLast)) {
|
|
2177
|
+
if (!m_chainman.ProcessNewBlockHeaders(nPoSTemperature, pfrom.lastAcceptedHeader, headers, state, m_chainparams, &pindexLast)) {
|
|
2169
2178
|
if (state.IsInvalid()) {
|
|
2170
2179
|
MaybePunishNodeForBlock(pfrom.GetId(), state, via_compact_block, "invalid header received");
|
|
2171
2180
|
return;
|
|
2172
2181
|
}
|
|
2173
2182
|
}
|
|
2183
|
+
pfrom.lastAcceptedHeader = headers.back().GetHash();
|
|
2174
2184
|
|
|
2175
2185
|
{
|
|
2176
2186
|
LOCK(cs_main);
|
|
@@ -2187,7 +2197,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2187
2197
|
// because it is set in UpdateBlockAvailability. Some nullptr checks
|
|
2188
2198
|
// are still present, however, as belt-and-suspenders.
|
|
2189
2199
|
|
|
2190
|
-
if (received_new_header && pindexLast->
|
|
2200
|
+
if (received_new_header && pindexLast->nChainTrust > m_chainman.ActiveChain().Tip()->nChainTrust) {
|
|
2191
2201
|
nodestate->m_last_block_announcement = GetTime();
|
|
2192
2202
|
}
|
|
2193
2203
|
|
|
@@ -2202,14 +2212,14 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2202
2212
|
|
|
2203
2213
|
// If this set of headers is valid and ends in a block with at least as
|
|
2204
2214
|
// much work as our tip, download as much as possible.
|
|
2205
|
-
if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->
|
|
2215
|
+
if (CanDirectFetch() && pindexLast->IsValid(BLOCK_VALID_TREE) && m_chainman.ActiveChain().Tip()->nChainTrust <= pindexLast->nChainTrust) {
|
|
2206
2216
|
std::vector<const CBlockIndex*> vToFetch;
|
|
2207
2217
|
const CBlockIndex *pindexWalk = pindexLast;
|
|
2208
2218
|
// Calculate all the blocks we'd need to switch to pindexLast, up to a limit.
|
|
2209
2219
|
while (pindexWalk && !m_chainman.ActiveChain().Contains(pindexWalk) && vToFetch.size() <= MAX_BLOCKS_IN_TRANSIT_PER_PEER) {
|
|
2210
2220
|
if (!(pindexWalk->nStatus & BLOCK_HAVE_DATA) &&
|
|
2211
2221
|
!IsBlockRequested(pindexWalk->GetBlockHash()) &&
|
|
2212
|
-
(!
|
|
2222
|
+
(!IsBTC16BIPsEnabled(pindexWalk->nTime) || State(pfrom.GetId())->fHaveWitness)) {
|
|
2213
2223
|
// We don't have this block, and it's not yet in flight.
|
|
2214
2224
|
vToFetch.push_back(pindexWalk);
|
|
2215
2225
|
}
|
|
@@ -2259,7 +2269,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2259
2269
|
if (m_chainman.ActiveChainstate().IsInitialBlockDownload() && nCount != MAX_HEADERS_RESULTS) {
|
|
2260
2270
|
// When nCount < MAX_HEADERS_RESULTS, we know we have no more
|
|
2261
2271
|
// headers to fetch from this peer.
|
|
2262
|
-
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->
|
|
2272
|
+
if (nodestate->pindexBestKnownBlock && nodestate->pindexBestKnownBlock->nChainTrust < nMinimumChainWork) {
|
|
2263
2273
|
// This peer has too little work on their headers chain to help
|
|
2264
2274
|
// us sync -- disconnect if it is an outbound disconnection
|
|
2265
2275
|
// candidate.
|
|
@@ -2281,7 +2291,7 @@ void PeerManagerImpl::ProcessHeadersMessage(CNode& pfrom, const Peer& peer,
|
|
|
2281
2291
|
// thus always subject to eviction under the bad/lagging chain logic.
|
|
2282
2292
|
// See ChainSyncTimeoutState.
|
|
2283
2293
|
if (!pfrom.fDisconnect && pfrom.IsFullOutboundConn() && nodestate->pindexBestKnownBlock != nullptr) {
|
|
2284
|
-
if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->
|
|
2294
|
+
if (m_outbound_peers_with_protect_from_disconnect < MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT && nodestate->pindexBestKnownBlock->nChainTrust >= m_chainman.ActiveChain().Tip()->nChainTrust && !nodestate->m_chain_sync.m_protect) {
|
|
2285
2295
|
LogPrint(BCLog::NET, "Protecting outbound peer=%d from eviction\n", pfrom.GetId());
|
|
2286
2296
|
nodestate->m_chain_sync.m_protect = true;
|
|
2287
2297
|
++m_outbound_peers_with_protect_from_disconnect;
|
|
@@ -2560,9 +2570,17 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
2560
2570
|
PeerRef peer = GetPeerRef(pfrom.GetId());
|
|
2561
2571
|
if (peer == nullptr) return;
|
|
2562
2572
|
|
|
2573
|
+
// set deserialization mode to read PoS flag in headers
|
|
2574
|
+
vRecv.SetType(vRecv.GetType() | SER_POSMARKER);
|
|
2575
|
+
|
|
2563
2576
|
if (msg_type == NetMsgType::VERSION) {
|
|
2564
|
-
|
|
2565
|
-
|
|
2577
|
+
auto it = mapPoSTemperature.find(pfrom.addr);
|
|
2578
|
+
if (it == mapPoSTemperature.end())
|
|
2579
|
+
mapPoSTemperature[pfrom.addr] = MAX_CONSECUTIVE_POS_HEADERS/4;
|
|
2580
|
+
// Each connection can only send one version message
|
|
2581
|
+
if (pfrom.nVersion != 0)
|
|
2582
|
+
{
|
|
2583
|
+
Misbehaving(pfrom.GetId(), 1, "redundant version message");
|
|
2566
2584
|
return;
|
|
2567
2585
|
}
|
|
2568
2586
|
|
|
@@ -3016,13 +3034,11 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3016
3034
|
for (CInv& inv : vInv) {
|
|
3017
3035
|
if (interruptMsgProc) return;
|
|
3018
3036
|
|
|
3019
|
-
//
|
|
3020
|
-
// Note that orphan parent fetching always uses MSG_TX GETDATAs regardless of the wtxidrelay setting.
|
|
3021
|
-
// This is fine as no INV messages are involved in that process.
|
|
3037
|
+
// ignore INVs that don't match wtxidrelay setting
|
|
3022
3038
|
if (State(pfrom.GetId())->m_wtxid_relay) {
|
|
3023
|
-
if (inv.
|
|
3039
|
+
if (inv.type == MSG_TX) continue;
|
|
3024
3040
|
} else {
|
|
3025
|
-
if (inv.
|
|
3041
|
+
if (inv.type == MSG_WTX) continue;
|
|
3026
3042
|
}
|
|
3027
3043
|
|
|
3028
3044
|
if (inv.IsMsgBlk()) {
|
|
@@ -3134,14 +3150,10 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3134
3150
|
if (pindex->GetBlockHash() == hashStop)
|
|
3135
3151
|
{
|
|
3136
3152
|
LogPrint(BCLog::NET, " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
const int nPrunedBlocksLikelyToHave = MIN_BLOCKS_TO_KEEP - 3600 / m_chainparams.GetConsensus().nPowTargetSpacing;
|
|
3142
|
-
if (fPruneMode && (!(pindex->nStatus & BLOCK_HAVE_DATA) || pindex->nHeight <= m_chainman.ActiveChain().Tip()->nHeight - nPrunedBlocksLikelyToHave))
|
|
3143
|
-
{
|
|
3144
|
-
LogPrint(BCLog::NET, " getblocks stopping, pruned or too old block at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
|
|
3153
|
+
// peercoin: tell downloading node about the latest block if it's
|
|
3154
|
+
// without risk being rejected due to stake connection check
|
|
3155
|
+
if (hashStop != m_chainman.ActiveChain().Tip()->GetBlockHash() && pindex->GetBlockTime() + Params().GetConsensus().nStakeMinAge > m_chainman.ActiveChain().Tip()->GetBlockTime())
|
|
3156
|
+
WITH_LOCK(peer->m_block_inv_mutex, peer->m_blocks_for_inv_relay.push_back(pindex->GetBlockHash()));
|
|
3145
3157
|
break;
|
|
3146
3158
|
}
|
|
3147
3159
|
WITH_LOCK(peer->m_block_inv_mutex, peer->m_blocks_for_inv_relay.push_back(pindex->GetBlockHash()));
|
|
@@ -3517,15 +3529,24 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3517
3529
|
}
|
|
3518
3530
|
}
|
|
3519
3531
|
|
|
3532
|
+
int32_t& nPoSTemperature = mapPoSTemperature[pfrom.addr];
|
|
3520
3533
|
const CBlockIndex *pindex = nullptr;
|
|
3521
3534
|
BlockValidationState state;
|
|
3522
|
-
if (!m_chainman.ProcessNewBlockHeaders({cmpctblock.header}, state, m_chainparams, &pindex)) {
|
|
3535
|
+
if (!m_chainman.ProcessNewBlockHeaders(nPoSTemperature, m_chainman.ActiveChain().Tip()->GetBlockHash(), {cmpctblock.header}, state, m_chainparams, &pindex)) {
|
|
3523
3536
|
if (state.IsInvalid()) {
|
|
3524
3537
|
MaybePunishNodeForBlock(pfrom.GetId(), state, /*via_compact_block*/ true, "invalid header via cmpctblock");
|
|
3525
3538
|
return;
|
|
3526
3539
|
}
|
|
3527
3540
|
}
|
|
3528
3541
|
|
|
3542
|
+
if (nPoSTemperature >= MAX_CONSECUTIVE_POS_HEADERS) {
|
|
3543
|
+
nPoSTemperature = (MAX_CONSECUTIVE_POS_HEADERS*3)/4;
|
|
3544
|
+
if (Params().NetworkIDString() != "test") {
|
|
3545
|
+
Misbehaving(pfrom.GetId(), 100, "too many consecutive pos headers");
|
|
3546
|
+
return;
|
|
3547
|
+
}
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3529
3550
|
// When we succeed in decoding a block's txids from a cmpctblock
|
|
3530
3551
|
// message we typically jump to the BLOCKTXN handling code, with a
|
|
3531
3552
|
// dummy (empty) BLOCKTXN message, to re-use the logic there in
|
|
@@ -3552,7 +3573,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3552
3573
|
|
|
3553
3574
|
// If this was a new header with more work than our tip, update the
|
|
3554
3575
|
// peer's last block announcement time
|
|
3555
|
-
if (received_new_header && pindex->
|
|
3576
|
+
if (received_new_header && pindex->nChainTrust > m_chainman.ActiveChain().Tip()->nChainTrust) {
|
|
3556
3577
|
nodestate->m_last_block_announcement = GetTime();
|
|
3557
3578
|
}
|
|
3558
3579
|
|
|
@@ -3562,8 +3583,8 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3562
3583
|
if (pindex->nStatus & BLOCK_HAVE_DATA) // Nothing to do here
|
|
3563
3584
|
return;
|
|
3564
3585
|
|
|
3565
|
-
if (pindex->
|
|
3566
|
-
pindex->nTx != 0) { // We had this block at some point
|
|
3586
|
+
if (pindex->nChainTrust <= m_chainman.ActiveChain().Tip()->nChainTrust || // We know something better
|
|
3587
|
+
pindex->nTx != 0) { // We had this block at some point
|
|
3567
3588
|
if (fAlreadyInFlight) {
|
|
3568
3589
|
// We requested this block for some reason, but our mempool will probably be useless
|
|
3569
3590
|
// so we just grab the block via normal getdata
|
|
@@ -3579,7 +3600,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3579
3600
|
return;
|
|
3580
3601
|
}
|
|
3581
3602
|
|
|
3582
|
-
if (
|
|
3603
|
+
if (IsBTC16BIPsEnabled(pindex->nTime) && !nodestate->fSupportsDesiredCmpctVersion) {
|
|
3583
3604
|
// Don't bother trying to process compact blocks from v1 peers
|
|
3584
3605
|
// after segwit activates.
|
|
3585
3606
|
return;
|
|
@@ -3797,9 +3818,32 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3797
3818
|
return;
|
|
3798
3819
|
}
|
|
3799
3820
|
headers.resize(nCount);
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3821
|
+
{
|
|
3822
|
+
LOCK(cs_main);
|
|
3823
|
+
int32_t& nPoSTemperature = mapPoSTemperature[pfrom.addr];
|
|
3824
|
+
int nTmpPoSTemperature = nPoSTemperature;
|
|
3825
|
+
for (unsigned int n = 0; n < nCount; n++) {
|
|
3826
|
+
vRecv >> headers[n];
|
|
3827
|
+
ReadCompactSize(vRecv); // ignore tx count; assume it is 0.
|
|
3828
|
+
ReadCompactSize(vRecv); // needed for vchBlockSig.
|
|
3829
|
+
|
|
3830
|
+
// peercoin: quick check to see if we should ban peers for PoS spam
|
|
3831
|
+
// note: at this point we don't know if PoW headers are valid - we just assume they are
|
|
3832
|
+
// so we need to update pfrom->nPoSTemperature once we actualy check them
|
|
3833
|
+
bool fPoS = headers[n].nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE;
|
|
3834
|
+
nTmpPoSTemperature += fPoS ? 1 : -POW_HEADER_COOLING;
|
|
3835
|
+
// peer cannot cool himself by PoW headers from other branches
|
|
3836
|
+
if (n == 0 && !fPoS && headers[n].hashPrevBlock != pfrom.lastAcceptedHeader)
|
|
3837
|
+
nTmpPoSTemperature += POW_HEADER_COOLING;
|
|
3838
|
+
nTmpPoSTemperature = std::max(nTmpPoSTemperature, 0);
|
|
3839
|
+
if (nTmpPoSTemperature >= MAX_CONSECUTIVE_POS_HEADERS) {
|
|
3840
|
+
nPoSTemperature = (MAX_CONSECUTIVE_POS_HEADERS*3)/4;
|
|
3841
|
+
if (Params().NetworkIDString() != "test") {
|
|
3842
|
+
Misbehaving(pfrom.GetId(), 100, "too many consecutive pos headers");
|
|
3843
|
+
return;
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3803
3847
|
}
|
|
3804
3848
|
|
|
3805
3849
|
return ProcessHeadersMessage(pfrom, *peer, headers, /*via_compact_block=*/false);
|
|
@@ -3813,25 +3857,115 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
3813
3857
|
return;
|
|
3814
3858
|
}
|
|
3815
3859
|
|
|
3816
|
-
std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>();
|
|
3817
|
-
vRecv >> *pblock;
|
|
3818
3860
|
|
|
3819
|
-
|
|
3861
|
+
std::shared_ptr<CBlock> pblock2 = std::make_shared<CBlock>();
|
|
3862
|
+
vRecv >> *pblock2;
|
|
3863
|
+
int64_t nTimeNow = GetTimeSeconds();
|
|
3864
|
+
|
|
3865
|
+
LogPrint(BCLog::NET, "received block %s peer=%d\n", pblock2->GetHash().ToString(), pfrom.GetId());
|
|
3820
3866
|
|
|
3821
|
-
bool forceProcessing = false;
|
|
3822
|
-
const uint256 hash(pblock->GetHash());
|
|
3823
3867
|
{
|
|
3868
|
+
const uint256 hash2(pblock2->GetHash());
|
|
3824
3869
|
LOCK(cs_main);
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3870
|
+
bool fRequested = mapBlocksInFlight.count(hash2);
|
|
3871
|
+
|
|
3872
|
+
CBlockIndex* headerPrev = m_chainman.m_blockman.LookupBlockIndex(pblock2->hashPrevBlock);
|
|
3873
|
+
if (!headerPrev) {
|
|
3874
|
+
LogPrint(BCLog::NET, "previous header not found");
|
|
3875
|
+
return;
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
if (!fRequested) {
|
|
3879
|
+
int32_t& nPoSTemperature = mapPoSTemperature[pfrom.addr];
|
|
3880
|
+
if (nPoSTemperature >= MAX_CONSECUTIVE_POS_HEADERS) {
|
|
3881
|
+
nPoSTemperature = (MAX_CONSECUTIVE_POS_HEADERS*3)/4;
|
|
3882
|
+
if (Params().NetworkIDString() != "test") {
|
|
3883
|
+
Misbehaving(pfrom.GetId(), 100, "too many consecutive pos headers");
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
if (pblock2->IsProofOfStake() && !m_chainman.ActiveChainstate().IsInitialBlockDownload())
|
|
3889
|
+
nPoSTemperature += 1;
|
|
3890
|
+
|
|
3891
|
+
if (!headerPrev->IsValid(BLOCK_VALID_TRANSACTIONS)) {
|
|
3892
|
+
RemoveBlockRequest(hash2);
|
|
3893
|
+
LogPrint(BCLog::NET, "this block does not connect to any valid known blocks");
|
|
3894
|
+
return;
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
// peercoin: store in memory until we can connect it to some chain
|
|
3898
|
+
WaitElement we; we.pblock = pblock2; we.time = nTimeNow;
|
|
3899
|
+
mapBlocksWait[headerPrev] = we;
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
static CBlockIndex* pindexLastAccepted = nullptr;
|
|
3903
|
+
if (pindexLastAccepted == nullptr)
|
|
3904
|
+
pindexLastAccepted = m_chainman.ActiveChain().Tip();
|
|
3905
|
+
bool fContinue = true;
|
|
3906
|
+
|
|
3907
|
+
// peercoin: accept as many blocks as we possibly can from mapBlocksWait
|
|
3908
|
+
while (fContinue) {
|
|
3909
|
+
fContinue = false;
|
|
3910
|
+
bool fSelected = false;
|
|
3911
|
+
bool forceProcessing = false;
|
|
3912
|
+
CBlockIndex* pindexPrev;
|
|
3913
|
+
std::shared_ptr<CBlock> pblock;
|
|
3914
|
+
|
|
3915
|
+
{
|
|
3916
|
+
LOCK(cs_main);
|
|
3917
|
+
// peercoin: try to select next block in a constant time
|
|
3918
|
+
std::map<CBlockIndex*, WaitElement>::iterator it = mapBlocksWait.find(pindexLastAccepted);
|
|
3919
|
+
if (it != mapBlocksWait.end() && pindexLastAccepted != nullptr) {
|
|
3920
|
+
pindexPrev = it->first;
|
|
3921
|
+
pblock = it->second.pblock;
|
|
3922
|
+
mapBlocksWait.erase(pindexPrev);
|
|
3923
|
+
fContinue = true;
|
|
3924
|
+
fSelected = true;
|
|
3925
|
+
} else
|
|
3926
|
+
// otherwise: try to scan for it
|
|
3927
|
+
for (auto& pair : mapBlocksWait) {
|
|
3928
|
+
pindexPrev = pair.first;
|
|
3929
|
+
pblock = pair.second.pblock;
|
|
3930
|
+
const uint256 hash(pblock->GetHash());
|
|
3931
|
+
// remove blocks that were not connected in 60 seconds
|
|
3932
|
+
if (nTimeNow > pair.second.time + 60) {
|
|
3933
|
+
mapBlocksWait.erase(pindexPrev);
|
|
3934
|
+
fContinue = true;
|
|
3935
|
+
RemoveBlockRequest(hash);
|
|
3936
|
+
break;
|
|
3937
|
+
}
|
|
3938
|
+
if (!pindexPrev->IsValid(BLOCK_VALID_TRANSACTIONS)) {
|
|
3939
|
+
if (pindexPrev->nStatus & BLOCK_FAILED_MASK) {
|
|
3940
|
+
mapBlocksWait.erase(pindexPrev); // prev block was rejected
|
|
3941
|
+
fContinue = true;
|
|
3942
|
+
RemoveBlockRequest(hash);
|
|
3943
|
+
break;
|
|
3944
|
+
}
|
|
3945
|
+
continue; // prev block was not (yet) accepted on disk, skip to next one
|
|
3946
|
+
}
|
|
3947
|
+
|
|
3948
|
+
mapBlocksWait.erase(pindexPrev);
|
|
3949
|
+
fContinue = true;
|
|
3950
|
+
fSelected = true;
|
|
3951
|
+
break;
|
|
3952
|
+
}
|
|
3953
|
+
if (!fSelected)
|
|
3954
|
+
continue;
|
|
3955
|
+
|
|
3956
|
+
const uint256 hash(pblock->GetHash());
|
|
3957
|
+
// Always process the block if we requested it, since we may
|
|
3958
|
+
// need it even when it's not a candidate for a new best tip.
|
|
3959
|
+
forceProcessing = IsBlockRequested(hash);
|
|
3960
|
+
RemoveBlockRequest(hash);
|
|
3961
|
+
// mapBlockSource is only used for punishing peers and setting
|
|
3962
|
+
// which peers send us compact blocks, so the race between here and
|
|
3963
|
+
// cs_main in ProcessNewBlock is fine.
|
|
3964
|
+
mapBlockSource.emplace(hash, std::make_pair(pfrom.GetId(), true));
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
ProcessBlock(pfrom, pblock, forceProcessing);
|
|
3968
|
+
}
|
|
3835
3969
|
return;
|
|
3836
3970
|
}
|
|
3837
3971
|
|
|
@@ -4048,7 +4182,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
4048
4182
|
if (pfrom.m_tx_relay != nullptr) {
|
|
4049
4183
|
pfrom.m_tx_relay->minFeeFilter = newFeeFilter;
|
|
4050
4184
|
}
|
|
4051
|
-
LogPrint(BCLog::NET, "received: feefilter of %
|
|
4185
|
+
LogPrint(BCLog::NET, "received: feefilter of %d satoshi from peer=%d\n", newFeeFilter, pfrom.GetId());
|
|
4052
4186
|
}
|
|
4053
4187
|
return;
|
|
4054
4188
|
}
|
|
@@ -4083,7 +4217,6 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
|
|
|
4083
4217
|
}
|
|
4084
4218
|
return;
|
|
4085
4219
|
}
|
|
4086
|
-
|
|
4087
4220
|
// Ignore unknown commands for extensibility
|
|
4088
4221
|
LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom.GetId());
|
|
4089
4222
|
return;
|
|
@@ -4224,13 +4357,13 @@ void PeerManagerImpl::ConsiderEviction(CNode& pto, std::chrono::seconds time_in_
|
|
|
4224
4357
|
// their chain has more work than ours, we should sync to it,
|
|
4225
4358
|
// unless it's invalid, in which case we should find that out and
|
|
4226
4359
|
// disconnect from them elsewhere).
|
|
4227
|
-
if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->
|
|
4360
|
+
if (state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainTrust >= m_chainman.ActiveChain().Tip()->nChainTrust) {
|
|
4228
4361
|
if (state.m_chain_sync.m_timeout != 0s) {
|
|
4229
4362
|
state.m_chain_sync.m_timeout = 0s;
|
|
4230
4363
|
state.m_chain_sync.m_work_header = nullptr;
|
|
4231
4364
|
state.m_chain_sync.m_sent_getheaders = false;
|
|
4232
4365
|
}
|
|
4233
|
-
} else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->
|
|
4366
|
+
} else if (state.m_chain_sync.m_timeout == 0s || (state.m_chain_sync.m_work_header != nullptr && state.pindexBestKnownBlock != nullptr && state.pindexBestKnownBlock->nChainTrust >= state.m_chain_sync.m_work_header->nChainTrust)) {
|
|
4234
4367
|
// Our best block known by this peer is behind our tip, and we're either noticing
|
|
4235
4368
|
// that for the first time, OR this peer was able to catch up to some earlier point
|
|
4236
4369
|
// where we checked against our tip.
|
|
@@ -4504,6 +4637,7 @@ void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::micros
|
|
|
4504
4637
|
}
|
|
4505
4638
|
}
|
|
4506
4639
|
|
|
4640
|
+
/*
|
|
4507
4641
|
void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds current_time)
|
|
4508
4642
|
{
|
|
4509
4643
|
AssertLockHeld(cs_main);
|
|
@@ -4546,6 +4680,7 @@ void PeerManagerImpl::MaybeSendFeefilter(CNode& pto, std::chrono::microseconds c
|
|
|
4546
4680
|
pto.m_tx_relay->m_next_send_feefilter = current_time + GetRandomDuration<std::chrono::microseconds>(MAX_FEEFILTER_CHANGE_DELAY);
|
|
4547
4681
|
}
|
|
4548
4682
|
}
|
|
4683
|
+
*/
|
|
4549
4684
|
|
|
4550
4685
|
namespace {
|
|
4551
4686
|
class CompareInvMempoolOrder
|
|
@@ -4833,7 +4968,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
4833
4968
|
if (fSendTrickle && pto->m_tx_relay->fSendMempool) {
|
|
4834
4969
|
auto vtxinfo = m_mempool.infoAll();
|
|
4835
4970
|
pto->m_tx_relay->fSendMempool = false;
|
|
4836
|
-
|
|
4971
|
+
CAmount filterrate = 0;
|
|
4837
4972
|
|
|
4838
4973
|
LOCK(pto->m_tx_relay->cs_filter);
|
|
4839
4974
|
|
|
@@ -4841,9 +4976,9 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
4841
4976
|
const uint256& hash = state.m_wtxid_relay ? txinfo.tx->GetWitnessHash() : txinfo.tx->GetHash();
|
|
4842
4977
|
CInv inv(state.m_wtxid_relay ? MSG_WTX : MSG_TX, hash);
|
|
4843
4978
|
pto->m_tx_relay->setInventoryTxToSend.erase(hash);
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4979
|
+
if (filterrate) {
|
|
4980
|
+
if (txinfo.fee < filterrate)
|
|
4981
|
+
continue;
|
|
4847
4982
|
}
|
|
4848
4983
|
if (pto->m_tx_relay->pfilter) {
|
|
4849
4984
|
if (!pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
|
@@ -4867,7 +5002,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
4867
5002
|
for (std::set<uint256>::iterator it = pto->m_tx_relay->setInventoryTxToSend.begin(); it != pto->m_tx_relay->setInventoryTxToSend.end(); it++) {
|
|
4868
5003
|
vInvTx.push_back(it);
|
|
4869
5004
|
}
|
|
4870
|
-
|
|
5005
|
+
CAmount filterrate = 0;
|
|
4871
5006
|
// Topologically and fee-rate sort the inventory we send for privacy and priority reasons.
|
|
4872
5007
|
// A heap is used so that not all items need sorting if only a few are being sent.
|
|
4873
5008
|
CompareInvMempoolOrder compareInvMempoolOrder(&m_mempool, state.m_wtxid_relay);
|
|
@@ -4897,7 +5032,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
4897
5032
|
auto txid = txinfo.tx->GetHash();
|
|
4898
5033
|
auto wtxid = txinfo.tx->GetWitnessHash();
|
|
4899
5034
|
// Peer told you to not send transactions at that feerate? Don't bother sending it.
|
|
4900
|
-
if (txinfo.fee < filterrate
|
|
5035
|
+
if (filterrate && txinfo.fee < filterrate) {
|
|
4901
5036
|
continue;
|
|
4902
5037
|
}
|
|
4903
5038
|
if (pto->m_tx_relay->pfilter && !pto->m_tx_relay->pfilter->IsRelevantAndUpdate(*txinfo.tx)) continue;
|
|
@@ -5011,7 +5146,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
5011
5146
|
NodeId staller = -1;
|
|
5012
5147
|
FindNextBlocksToDownload(pto->GetId(), MAX_BLOCKS_IN_TRANSIT_PER_PEER - state.nBlocksInFlight, vToDownload, staller);
|
|
5013
5148
|
for (const CBlockIndex *pindex : vToDownload) {
|
|
5014
|
-
uint32_t nFetchFlags = GetFetchFlags(*pto);
|
|
5149
|
+
uint32_t nFetchFlags = IsBTC16BIPsEnabled(pindex->nTime) ? GetFetchFlags(*pto) : false;
|
|
5015
5150
|
vGetData.push_back(CInv(MSG_BLOCK | nFetchFlags, pindex->GetBlockHash()));
|
|
5016
5151
|
BlockRequested(pto->GetId(), *pindex);
|
|
5017
5152
|
LogPrint(BCLog::NET, "Requesting block %s (%d) peer=%d\n", pindex->GetBlockHash().ToString(),
|
|
@@ -5051,11 +5186,8 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
|
|
|
5051
5186
|
}
|
|
5052
5187
|
}
|
|
5053
5188
|
|
|
5054
|
-
|
|
5055
5189
|
if (!vGetData.empty())
|
|
5056
5190
|
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
|
|
5057
|
-
|
|
5058
|
-
MaybeSendFeefilter(*pto, current_time);
|
|
5059
5191
|
} // release cs_main
|
|
5060
5192
|
return true;
|
|
5061
5193
|
}
|
|
@@ -19,7 +19,7 @@ public:
|
|
|
19
19
|
{
|
|
20
20
|
CSerializedNetMsg msg;
|
|
21
21
|
msg.m_type = std::move(msg_type);
|
|
22
|
-
CVectorWriter{ SER_NETWORK, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... };
|
|
22
|
+
CVectorWriter{ SER_NETWORK | SER_POSMARKER, nFlags | nVersion, msg.data, 0, std::forward<Args>(args)... };
|
|
23
23
|
return msg;
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <flatfile.h>
|
|
12
12
|
#include <fs.h>
|
|
13
13
|
#include <hash.h>
|
|
14
|
+
#include <kernel.h>
|
|
14
15
|
#include <pow.h>
|
|
15
16
|
#include <reverse_iterator.h>
|
|
16
17
|
#include <shutdown.h>
|
|
@@ -24,9 +25,6 @@
|
|
|
24
25
|
namespace node {
|
|
25
26
|
std::atomic_bool fImporting(false);
|
|
26
27
|
std::atomic_bool fReindex(false);
|
|
27
|
-
bool fHavePruned = false;
|
|
28
|
-
bool fPruneMode = false;
|
|
29
|
-
uint64_t nPruneTarget = 0;
|
|
30
28
|
|
|
31
29
|
static FILE* OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false);
|
|
32
30
|
static FlatFileSeq BlockFileSeq();
|
|
@@ -65,9 +63,11 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
|
|
|
65
63
|
pindexNew->BuildSkip();
|
|
66
64
|
}
|
|
67
65
|
pindexNew->nTimeMax = (pindexNew->pprev ? std::max(pindexNew->pprev->nTimeMax, pindexNew->nTime) : pindexNew->nTime);
|
|
68
|
-
|
|
66
|
+
if (block.nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE)
|
|
67
|
+
pindexNew->SetProofOfStake();
|
|
68
|
+
pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + GetBlockTrust(*pindexNew);
|
|
69
69
|
pindexNew->RaiseValidity(BLOCK_VALID_TREE);
|
|
70
|
-
if (pindexBestHeader == nullptr || pindexBestHeader->
|
|
70
|
+
if (pindexBestHeader == nullptr || pindexBestHeader->nChainTrust < pindexNew->nChainTrust)
|
|
71
71
|
pindexBestHeader = pindexNew;
|
|
72
72
|
|
|
73
73
|
m_dirty_blockindex.insert(pindexNew);
|
|
@@ -75,122 +75,6 @@ CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block)
|
|
|
75
75
|
return pindexNew;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
void BlockManager::PruneOneBlockFile(const int fileNumber)
|
|
79
|
-
{
|
|
80
|
-
AssertLockHeld(cs_main);
|
|
81
|
-
LOCK(cs_LastBlockFile);
|
|
82
|
-
|
|
83
|
-
for (const auto& entry : m_block_index) {
|
|
84
|
-
CBlockIndex* pindex = entry.second;
|
|
85
|
-
if (pindex->nFile == fileNumber) {
|
|
86
|
-
pindex->nStatus &= ~BLOCK_HAVE_DATA;
|
|
87
|
-
pindex->nStatus &= ~BLOCK_HAVE_UNDO;
|
|
88
|
-
pindex->nFile = 0;
|
|
89
|
-
pindex->nDataPos = 0;
|
|
90
|
-
pindex->nUndoPos = 0;
|
|
91
|
-
m_dirty_blockindex.insert(pindex);
|
|
92
|
-
|
|
93
|
-
// Prune from m_blocks_unlinked -- any block we prune would have
|
|
94
|
-
// to be downloaded again in order to consider its chain, at which
|
|
95
|
-
// point it would be considered as a candidate for
|
|
96
|
-
// m_blocks_unlinked or setBlockIndexCandidates.
|
|
97
|
-
auto range = m_blocks_unlinked.equal_range(pindex->pprev);
|
|
98
|
-
while (range.first != range.second) {
|
|
99
|
-
std::multimap<CBlockIndex*, CBlockIndex*>::iterator _it = range.first;
|
|
100
|
-
range.first++;
|
|
101
|
-
if (_it->second == pindex) {
|
|
102
|
-
m_blocks_unlinked.erase(_it);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
m_blockfile_info[fileNumber].SetNull();
|
|
109
|
-
m_dirty_fileinfo.insert(fileNumber);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
void BlockManager::FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height)
|
|
113
|
-
{
|
|
114
|
-
assert(fPruneMode && nManualPruneHeight > 0);
|
|
115
|
-
|
|
116
|
-
LOCK2(cs_main, cs_LastBlockFile);
|
|
117
|
-
if (chain_tip_height < 0) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip)
|
|
122
|
-
unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP);
|
|
123
|
-
int count = 0;
|
|
124
|
-
for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) {
|
|
125
|
-
if (m_blockfile_info[fileNumber].nSize == 0 || m_blockfile_info[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
PruneOneBlockFile(fileNumber);
|
|
129
|
-
setFilesToPrune.insert(fileNumber);
|
|
130
|
-
count++;
|
|
131
|
-
}
|
|
132
|
-
LogPrintf("Prune (Manual): prune_height=%d removed %d blk/rev pairs\n", nLastBlockWeCanPrune, count);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
void BlockManager::FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd)
|
|
136
|
-
{
|
|
137
|
-
LOCK2(cs_main, cs_LastBlockFile);
|
|
138
|
-
if (chain_tip_height < 0 || nPruneTarget == 0) {
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
if ((uint64_t)chain_tip_height <= nPruneAfterHeight) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
unsigned int nLastBlockWeCanPrune{(unsigned)std::min(prune_height, chain_tip_height - static_cast<int>(MIN_BLOCKS_TO_KEEP))};
|
|
146
|
-
uint64_t nCurrentUsage = CalculateCurrentUsage();
|
|
147
|
-
// We don't check to prune until after we've allocated new space for files
|
|
148
|
-
// So we should leave a buffer under our target to account for another allocation
|
|
149
|
-
// before the next pruning.
|
|
150
|
-
uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE;
|
|
151
|
-
uint64_t nBytesToPrune;
|
|
152
|
-
int count = 0;
|
|
153
|
-
|
|
154
|
-
if (nCurrentUsage + nBuffer >= nPruneTarget) {
|
|
155
|
-
// On a prune event, the chainstate DB is flushed.
|
|
156
|
-
// To avoid excessive prune events negating the benefit of high dbcache
|
|
157
|
-
// values, we should not prune too rapidly.
|
|
158
|
-
// So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon.
|
|
159
|
-
if (is_ibd) {
|
|
160
|
-
// Since this is only relevant during IBD, we use a fixed 10%
|
|
161
|
-
nBuffer += nPruneTarget / 10;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
for (int fileNumber = 0; fileNumber < m_last_blockfile; fileNumber++) {
|
|
165
|
-
nBytesToPrune = m_blockfile_info[fileNumber].nSize + m_blockfile_info[fileNumber].nUndoSize;
|
|
166
|
-
|
|
167
|
-
if (m_blockfile_info[fileNumber].nSize == 0) {
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target?
|
|
172
|
-
break;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning
|
|
176
|
-
if (m_blockfile_info[fileNumber].nHeightLast > nLastBlockWeCanPrune) {
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
PruneOneBlockFile(fileNumber);
|
|
181
|
-
// Queue up the files for removal
|
|
182
|
-
setFilesToPrune.insert(fileNumber);
|
|
183
|
-
nCurrentUsage -= nBytesToPrune;
|
|
184
|
-
count++;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
LogPrint(BCLog::PRUNE, "Prune: target=%dMiB actual=%dMiB diff=%dMiB max_prune_height=%d removed %d blk/rev pairs\n",
|
|
189
|
-
nPruneTarget/1024/1024, nCurrentUsage/1024/1024,
|
|
190
|
-
((int64_t)nPruneTarget - (int64_t)nCurrentUsage)/1024/1024,
|
|
191
|
-
nLastBlockWeCanPrune, count);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
78
|
CBlockIndex* BlockManager::InsertBlockIndex(const uint256& hash)
|
|
195
79
|
{
|
|
196
80
|
AssertLockHeld(cs_main);
|
|
@@ -221,7 +105,7 @@ bool BlockManager::LoadBlockIndex(
|
|
|
221
105
|
return false;
|
|
222
106
|
}
|
|
223
107
|
|
|
224
|
-
// Calculate
|
|
108
|
+
// Calculate nChainTrust
|
|
225
109
|
std::vector<std::pair<int, CBlockIndex*>> vSortedByHeight;
|
|
226
110
|
vSortedByHeight.reserve(m_block_index.size());
|
|
227
111
|
for (const std::pair<const uint256, CBlockIndex*>& item : m_block_index) {
|
|
@@ -253,13 +137,12 @@ bool BlockManager::LoadBlockIndex(
|
|
|
253
137
|
for (const std::pair<int, CBlockIndex*>& item : vSortedByHeight) {
|
|
254
138
|
if (ShutdownRequested()) return false;
|
|
255
139
|
CBlockIndex* pindex = item.second;
|
|
256
|
-
pindex->
|
|
140
|
+
pindex->nChainTrust = (pindex->pprev ? pindex->pprev->nChainTrust : 0) + GetBlockTrust(*pindex);
|
|
257
141
|
pindex->nTimeMax = (pindex->pprev ? std::max(pindex->pprev->nTimeMax, pindex->nTime) : pindex->nTime);
|
|
258
142
|
|
|
259
143
|
// We can link the chain of blocks for which we've received transactions at some point, or
|
|
260
144
|
// blocks that are assumed-valid on the basis of snapshot load (see
|
|
261
145
|
// PopulateAndValidateSnapshot()).
|
|
262
|
-
// Pruned nodes may have deleted the block.
|
|
263
146
|
if (pindex->nTx > 0) {
|
|
264
147
|
if (pindex->pprev) {
|
|
265
148
|
if (pindex->pprev->nChainTx > 0) {
|
|
@@ -310,7 +193,7 @@ bool BlockManager::LoadBlockIndex(
|
|
|
310
193
|
}
|
|
311
194
|
}
|
|
312
195
|
}
|
|
313
|
-
if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->
|
|
196
|
+
if (pindex->nStatus & BLOCK_FAILED_MASK && (!chainman.m_best_invalid || pindex->nChainTrust > chainman.m_best_invalid->nChainTrust)) {
|
|
314
197
|
chainman.m_best_invalid = pindex;
|
|
315
198
|
}
|
|
316
199
|
if (pindex->pprev) {
|
|
@@ -318,6 +201,12 @@ bool BlockManager::LoadBlockIndex(
|
|
|
318
201
|
}
|
|
319
202
|
if (pindex->IsValid(BLOCK_VALID_TREE) && (pindexBestHeader == nullptr || CBlockIndexWorkComparator()(pindexBestHeader, pindex)))
|
|
320
203
|
pindexBestHeader = pindex;
|
|
204
|
+
|
|
205
|
+
// peercoin: calculate stake modifier checksum
|
|
206
|
+
pindex->nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
|
|
207
|
+
if (chainman.ActiveChain().Contains(pindex))
|
|
208
|
+
if (!CheckStakeModifierCheckpoints(pindex->nHeight, pindex->nStakeModifierChecksum))
|
|
209
|
+
return error("LoadBlockIndex() : Failed stake modifier checkpoint height=%d, modifier=0x%016llx", pindex->nHeight, pindex->nStakeModifier);
|
|
321
210
|
}
|
|
322
211
|
|
|
323
212
|
return true;
|
|
@@ -399,12 +288,6 @@ bool BlockManager::LoadBlockIndexDB(ChainstateManager& chainman)
|
|
|
399
288
|
}
|
|
400
289
|
}
|
|
401
290
|
|
|
402
|
-
// Check whether we have ever pruned block & undo files
|
|
403
|
-
m_block_tree_db->ReadFlag("prunedblockfiles", fHavePruned);
|
|
404
|
-
if (fHavePruned) {
|
|
405
|
-
LogPrintf("LoadBlockIndexDB(): Block files have previously been pruned\n");
|
|
406
|
-
}
|
|
407
|
-
|
|
408
291
|
// Check whether we need to continue reindexing
|
|
409
292
|
bool fReindexing = false;
|
|
410
293
|
m_block_tree_db->ReadReindexing(fReindexing);
|
|
@@ -427,55 +310,6 @@ CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
|
|
|
427
310
|
return nullptr;
|
|
428
311
|
}
|
|
429
312
|
|
|
430
|
-
bool IsBlockPruned(const CBlockIndex* pblockindex)
|
|
431
|
-
{
|
|
432
|
-
AssertLockHeld(::cs_main);
|
|
433
|
-
return (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// If we're using -prune with -reindex, then delete block files that will be ignored by the
|
|
437
|
-
// reindex. Since reindexing works by starting at block file 0 and looping until a blockfile
|
|
438
|
-
// is missing, do the same here to delete any later block files after a gap. Also delete all
|
|
439
|
-
// rev files since they'll be rewritten by the reindex anyway. This ensures that m_blockfile_info
|
|
440
|
-
// is in sync with what's actually on disk by the time we start downloading, so that pruning
|
|
441
|
-
// works correctly.
|
|
442
|
-
void CleanupBlockRevFiles()
|
|
443
|
-
{
|
|
444
|
-
std::map<std::string, fs::path> mapBlockFiles;
|
|
445
|
-
|
|
446
|
-
// Glob all blk?????.dat and rev?????.dat files from the blocks directory.
|
|
447
|
-
// Remove the rev files immediately and insert the blk file paths into an
|
|
448
|
-
// ordered map keyed by block file index.
|
|
449
|
-
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
|
|
450
|
-
fs::path blocksdir = gArgs.GetBlocksDirPath();
|
|
451
|
-
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
|
|
452
|
-
const std::string path = fs::PathToString(it->path().filename());
|
|
453
|
-
if (fs::is_regular_file(*it) &&
|
|
454
|
-
path.length() == 12 &&
|
|
455
|
-
path.substr(8,4) == ".dat")
|
|
456
|
-
{
|
|
457
|
-
if (path.substr(0, 3) == "blk") {
|
|
458
|
-
mapBlockFiles[path.substr(3, 5)] = it->path();
|
|
459
|
-
} else if (path.substr(0, 3) == "rev") {
|
|
460
|
-
remove(it->path());
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
// Remove all block files that aren't part of a contiguous set starting at
|
|
466
|
-
// zero by walking the ordered map (keys are block file indices) by
|
|
467
|
-
// keeping a separate counter. Once we hit a gap (or if 0 doesn't exist)
|
|
468
|
-
// start removing block files.
|
|
469
|
-
int nContigCounter = 0;
|
|
470
|
-
for (const std::pair<const std::string, fs::path>& item : mapBlockFiles) {
|
|
471
|
-
if (LocaleIndependentAtoi<int>(item.first) == nContigCounter) {
|
|
472
|
-
nContigCounter++;
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
remove(item.second);
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
|
|
479
313
|
CBlockFileInfo* BlockManager::GetBlockFileInfo(size_t n)
|
|
480
314
|
{
|
|
481
315
|
LOCK(cs_LastBlockFile);
|
|
@@ -576,16 +410,6 @@ uint64_t BlockManager::CalculateCurrentUsage()
|
|
|
576
410
|
return retval;
|
|
577
411
|
}
|
|
578
412
|
|
|
579
|
-
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune)
|
|
580
|
-
{
|
|
581
|
-
for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
|
|
582
|
-
FlatFilePos pos(*it, 0);
|
|
583
|
-
fs::remove(BlockFileSeq().FileName(pos));
|
|
584
|
-
fs::remove(UndoFileSeq().FileName(pos));
|
|
585
|
-
LogPrint(BCLog::BLOCKSTORE, "Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
413
|
static FlatFileSeq BlockFileSeq()
|
|
590
414
|
{
|
|
591
415
|
return FlatFileSeq(gArgs.GetBlocksDirPath(), "blk", gArgs.GetBoolArg("-fastprune", false) ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
|
|
@@ -658,9 +482,6 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne
|
|
|
658
482
|
if (out_of_space) {
|
|
659
483
|
return AbortNode("Disk space is too low!", _("Disk space is too low!"));
|
|
660
484
|
}
|
|
661
|
-
if (bytes_allocated != 0 && fPruneMode) {
|
|
662
|
-
m_check_for_pruning = true;
|
|
663
|
-
}
|
|
664
485
|
}
|
|
665
486
|
|
|
666
487
|
m_dirty_fileinfo.insert(nFile);
|
|
@@ -682,9 +503,6 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP
|
|
|
682
503
|
if (out_of_space) {
|
|
683
504
|
return AbortNode(state, "Disk space is too low!", _("Disk space is too low!"));
|
|
684
505
|
}
|
|
685
|
-
if (bytes_allocated != 0 && fPruneMode) {
|
|
686
|
-
m_check_for_pruning = true;
|
|
687
|
-
}
|
|
688
506
|
|
|
689
507
|
return true;
|
|
690
508
|
}
|
|
@@ -760,7 +578,7 @@ bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::P
|
|
|
760
578
|
}
|
|
761
579
|
|
|
762
580
|
// Check the header
|
|
763
|
-
if (!CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) {
|
|
581
|
+
if (block.IsProofOfWork() && !CheckProofOfWork(block.GetHash(), block.nBits, consensusParams)) {
|
|
764
582
|
return error("ReadBlockFromDisk: Errors in block header at %s", pos.ToString());
|
|
765
583
|
}
|
|
766
584
|
|
|
@@ -769,6 +587,10 @@ bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::P
|
|
|
769
587
|
return error("ReadBlockFromDisk: Errors in block solution at %s", pos.ToString());
|
|
770
588
|
}
|
|
771
589
|
|
|
590
|
+
// Set flag if proof of stake
|
|
591
|
+
if (block.IsProofOfStake())
|
|
592
|
+
block.nFlags |= CBlockIndex::BLOCK_PROOF_OF_STAKE;
|
|
593
|
+
|
|
772
594
|
return true;
|
|
773
595
|
}
|
|
774
596
|
|
|
@@ -44,13 +44,6 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
|
|
44
44
|
|
|
45
45
|
extern std::atomic_bool fImporting;
|
|
46
46
|
extern std::atomic_bool fReindex;
|
|
47
|
-
/** Pruning-related variables and constants */
|
|
48
|
-
/** True if any block files have ever been pruned. */
|
|
49
|
-
extern bool fHavePruned;
|
|
50
|
-
/** True if we're running in -prune mode. */
|
|
51
|
-
extern bool fPruneMode;
|
|
52
|
-
/** Number of MiB of block files that we're trying to stay below. */
|
|
53
|
-
extern uint64_t nPruneTarget;
|
|
54
47
|
|
|
55
48
|
typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap;
|
|
56
49
|
|
|
@@ -76,37 +69,9 @@ private:
|
|
|
76
69
|
bool FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown);
|
|
77
70
|
bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize);
|
|
78
71
|
|
|
79
|
-
/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */
|
|
80
|
-
void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight, int chain_tip_height);
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Prune block and undo files (blk???.dat and rev???.dat) so that the disk space used is less than a user-defined target.
|
|
84
|
-
* The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new
|
|
85
|
-
* space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex
|
|
86
|
-
* (which in this case means the blockchain must be re-downloaded.)
|
|
87
|
-
*
|
|
88
|
-
* Pruning functions are called from FlushStateToDisk when the m_check_for_pruning flag has been set.
|
|
89
|
-
* Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.)
|
|
90
|
-
* Pruning cannot take place until the longest chain is at least a certain length (CChainParams::nPruneAfterHeight).
|
|
91
|
-
* Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip.
|
|
92
|
-
* The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files.
|
|
93
|
-
* A db flag records the fact that at least some block files have been pruned.
|
|
94
|
-
*
|
|
95
|
-
* @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned
|
|
96
|
-
*/
|
|
97
|
-
void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, int prune_height, bool is_ibd);
|
|
98
|
-
|
|
99
72
|
RecursiveMutex cs_LastBlockFile;
|
|
100
73
|
std::vector<CBlockFileInfo> m_blockfile_info;
|
|
101
74
|
int m_last_blockfile = 0;
|
|
102
|
-
/** Global flag to indicate we should check to see if there are
|
|
103
|
-
* block/undo files that should be deleted. Set on startup
|
|
104
|
-
* or if we allocate more file space when we're in prune mode
|
|
105
|
-
*/
|
|
106
|
-
bool m_check_for_pruning = false;
|
|
107
|
-
|
|
108
|
-
/** Dirty block index entries. */
|
|
109
|
-
std::set<CBlockIndex*> m_dirty_blockindex;
|
|
110
75
|
|
|
111
76
|
/** Dirty block file entries. */
|
|
112
77
|
std::set<int> m_dirty_fileinfo;
|
|
@@ -116,12 +81,13 @@ public:
|
|
|
116
81
|
|
|
117
82
|
/**
|
|
118
83
|
* All pairs A->B, where A (or one of its ancestors) misses transactions, but B has transactions.
|
|
119
|
-
* Pruned nodes may have entries where B is missing data.
|
|
120
84
|
*/
|
|
121
85
|
std::multimap<CBlockIndex*, CBlockIndex*> m_blocks_unlinked;
|
|
122
|
-
|
|
123
86
|
std::unique_ptr<CBlockTreeDB> m_block_tree_db GUARDED_BY(::cs_main);
|
|
124
87
|
|
|
88
|
+
/** Dirty block index entries. */
|
|
89
|
+
std::set<CBlockIndex*> m_dirty_blockindex;
|
|
90
|
+
|
|
125
91
|
bool WriteBlockIndexDB() EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
|
126
92
|
bool LoadBlockIndexDB(ChainstateManager& chainman) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
|
127
93
|
|
|
@@ -141,9 +107,6 @@ public:
|
|
|
141
107
|
/** Create a new block index entry for a given block hash */
|
|
142
108
|
CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
143
109
|
|
|
144
|
-
//! Mark one block file as pruned (modify associated database entries)
|
|
145
|
-
void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
146
|
-
|
|
147
110
|
CBlockIndex* LookupBlockIndex(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
148
111
|
|
|
149
112
|
/** Get block file info entry for one block file */
|
|
@@ -166,21 +129,11 @@ public:
|
|
|
166
129
|
}
|
|
167
130
|
};
|
|
168
131
|
|
|
169
|
-
//! Check whether the block associated with this index entry is pruned or not.
|
|
170
|
-
bool IsBlockPruned(const CBlockIndex* pblockindex) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
|
|
171
|
-
|
|
172
|
-
void CleanupBlockRevFiles();
|
|
173
|
-
|
|
174
132
|
/** Open a block file (blk?????.dat) */
|
|
175
133
|
FILE* OpenBlockFile(const FlatFilePos& pos, bool fReadOnly = false);
|
|
176
134
|
/** Translation to a filesystem path */
|
|
177
135
|
fs::path GetBlockPosFilename(const FlatFilePos& pos);
|
|
178
136
|
|
|
179
|
-
/**
|
|
180
|
-
* Actually unlink the specified files
|
|
181
|
-
*/
|
|
182
|
-
void UnlinkPrunedFiles(const std::set<int>& setFilesToPrune);
|
|
183
|
-
|
|
184
137
|
/** Functions for disk access for blocks */
|
|
185
138
|
bool ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos, const Consensus::Params& consensusParams);
|
|
186
139
|
bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams);
|
|
@@ -12,7 +12,6 @@ namespace node {
|
|
|
12
12
|
std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
|
|
13
13
|
ChainstateManager& chainman,
|
|
14
14
|
CTxMemPool* mempool,
|
|
15
|
-
bool fPruneMode,
|
|
16
15
|
const Consensus::Params& consensus_params,
|
|
17
16
|
bool fReindexChainState,
|
|
18
17
|
int64_t nBlockTreeDBCache,
|
|
@@ -42,16 +41,11 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
|
|
|
42
41
|
|
|
43
42
|
if (fReset) {
|
|
44
43
|
pblocktree->WriteReindexing(true);
|
|
45
|
-
//If we're reindexing in prune mode, wipe away unusable block files and all undo data files
|
|
46
|
-
if (fPruneMode)
|
|
47
|
-
CleanupBlockRevFiles();
|
|
48
44
|
}
|
|
49
45
|
|
|
50
46
|
if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
|
|
51
47
|
|
|
52
|
-
// LoadBlockIndex
|
|
53
|
-
// block file from disk.
|
|
54
|
-
// Note that it also sets fReindex based on the disk flag!
|
|
48
|
+
// Note that LoadBlockIndex also sets fReindex based on the disk flag!
|
|
55
49
|
// From here on out fReindex and fReset mean something different!
|
|
56
50
|
if (!chainman.LoadBlockIndex()) {
|
|
57
51
|
if (shutdown_requested && shutdown_requested()) return ChainstateLoadingError::SHUTDOWN_PROBED;
|
|
@@ -63,12 +57,6 @@ std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
|
|
|
63
57
|
return ChainstateLoadingError::ERROR_BAD_GENESIS_BLOCK;
|
|
64
58
|
}
|
|
65
59
|
|
|
66
|
-
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks
|
|
67
|
-
// in the past, but is now trying to run unpruned.
|
|
68
|
-
if (fHavePruned && !fPruneMode) {
|
|
69
|
-
return ChainstateLoadingError::ERROR_PRUNED_NEEDS_REINDEX;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
60
|
// At this point blocktree args are consistent with what's on disk.
|
|
73
61
|
// If we're not mid-reindex (based on disk + args), add a genesis block on disk
|
|
74
62
|
// (otherwise we use the one already on disk).
|
|
@@ -19,7 +19,6 @@ namespace node {
|
|
|
19
19
|
enum class ChainstateLoadingError {
|
|
20
20
|
ERROR_LOADING_BLOCK_DB,
|
|
21
21
|
ERROR_BAD_GENESIS_BLOCK,
|
|
22
|
-
ERROR_PRUNED_NEEDS_REINDEX,
|
|
23
22
|
ERROR_LOAD_GENESIS_BLOCK_FAILED,
|
|
24
23
|
ERROR_CHAINSTATE_UPGRADE_FAILED,
|
|
25
24
|
ERROR_REPLAYBLOCKS_FAILED,
|
|
@@ -58,7 +57,6 @@ enum class ChainstateLoadingError {
|
|
|
58
57
|
std::optional<ChainstateLoadingError> LoadChainstate(bool fReset,
|
|
59
58
|
ChainstateManager& chainman,
|
|
60
59
|
CTxMemPool* mempool,
|
|
61
|
-
bool fPruneMode,
|
|
62
60
|
const Consensus::Params& consensus_params,
|
|
63
61
|
bool fReindexChainState,
|
|
64
62
|
int64_t nBlockTreeDBCache,
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
#include <interfaces/chain.h>
|
|
10
10
|
#include <net.h>
|
|
11
11
|
#include <net_processing.h>
|
|
12
|
-
#include <policy/fees.h>
|
|
13
12
|
#include <scheduler.h>
|
|
14
13
|
#include <txmempool.h>
|
|
15
14
|
#include <validation.h>
|
|
@@ -10,21 +10,23 @@
|
|
|
10
10
|
#include <memory>
|
|
11
11
|
#include <vector>
|
|
12
12
|
|
|
13
|
+
#include <interfaces/init.h>
|
|
14
|
+
#include <interfaces/chain.h>
|
|
15
|
+
#include <interfaces/wallet.h>
|
|
16
|
+
|
|
13
17
|
class ArgsManager;
|
|
14
18
|
class BanMan;
|
|
15
19
|
class AddrMan;
|
|
16
|
-
class CBlockPolicyEstimator;
|
|
17
20
|
class CConnman;
|
|
18
21
|
class CScheduler;
|
|
19
22
|
class CTxMemPool;
|
|
20
23
|
class ChainstateManager;
|
|
21
24
|
class PeerManager;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
} // namespace interfaces
|
|
25
|
+
|
|
26
|
+
using interfaces::Chain;
|
|
27
|
+
using interfaces::ChainClient;
|
|
28
|
+
using interfaces::Init;
|
|
29
|
+
using interfaces::WalletLoader;
|
|
28
30
|
|
|
29
31
|
namespace node {
|
|
30
32
|
//! NodeContext struct containing references to chain state and connection
|
|
@@ -43,7 +45,6 @@ struct NodeContext {
|
|
|
43
45
|
std::unique_ptr<AddrMan> addrman;
|
|
44
46
|
std::unique_ptr<CConnman> connman;
|
|
45
47
|
std::unique_ptr<CTxMemPool> mempool;
|
|
46
|
-
std::unique_ptr<CBlockPolicyEstimator> fee_estimator;
|
|
47
48
|
std::unique_ptr<PeerManager> peerman;
|
|
48
49
|
std::unique_ptr<ChainstateManager> chainman;
|
|
49
50
|
std::unique_ptr<BanMan> banman;
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
#include <banman.h>
|
|
7
7
|
#include <chain.h>
|
|
8
8
|
#include <chainparams.h>
|
|
9
|
-
#include <deploymentstatus.h>
|
|
10
9
|
#include <external_signer.h>
|
|
11
10
|
#include <init.h>
|
|
12
11
|
#include <interfaces/chain.h>
|
|
@@ -23,10 +22,7 @@
|
|
|
23
22
|
#include <node/context.h>
|
|
24
23
|
#include <node/transaction.h>
|
|
25
24
|
#include <node/ui_interface.h>
|
|
26
|
-
#include <policy/feerate.h>
|
|
27
|
-
#include <policy/fees.h>
|
|
28
25
|
#include <policy/policy.h>
|
|
29
|
-
#include <policy/rbf.h>
|
|
30
26
|
#include <policy/settings.h>
|
|
31
27
|
#include <primitives/block.h>
|
|
32
28
|
#include <primitives/transaction.h>
|
|
@@ -81,8 +77,8 @@ private:
|
|
|
81
77
|
class NodeImpl : public Node
|
|
82
78
|
{
|
|
83
79
|
private:
|
|
84
|
-
ChainstateManager& chainman() { return *Assert(m_context->chainman); }
|
|
85
80
|
public:
|
|
81
|
+
ChainstateManager& chainman() override { return *Assert(m_context->chainman); }
|
|
86
82
|
explicit NodeImpl(NodeContext& context) { setContext(&context); }
|
|
87
83
|
void initLogging() override { InitLogging(*Assert(m_context->args)); }
|
|
88
84
|
void initParameterInteraction() override { InitParameterInteraction(*Assert(m_context->args)); }
|
|
@@ -258,7 +254,6 @@ public:
|
|
|
258
254
|
}
|
|
259
255
|
}
|
|
260
256
|
bool getNetworkActive() override { return m_context->connman && m_context->connman->GetNetworkActive(); }
|
|
261
|
-
CFeeRate getDustRelayFee() override { return ::dustRelayFee; }
|
|
262
257
|
UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override
|
|
263
258
|
{
|
|
264
259
|
JSONRPCRequest req;
|
|
@@ -276,9 +271,9 @@ public:
|
|
|
276
271
|
LOCK(::cs_main);
|
|
277
272
|
return chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin);
|
|
278
273
|
}
|
|
279
|
-
TransactionError broadcastTransaction(CTransactionRef tx,
|
|
274
|
+
TransactionError broadcastTransaction(CTransactionRef tx, std::string& err_string) override
|
|
280
275
|
{
|
|
281
|
-
return BroadcastTransaction(*m_context, std::move(tx), err_string,
|
|
276
|
+
return BroadcastTransaction(*m_context, std::move(tx), err_string, /*relay=*/ true, /*wait_callback=*/ false);
|
|
282
277
|
}
|
|
283
278
|
WalletLoader& walletLoader() override
|
|
284
279
|
{
|
|
@@ -314,7 +309,9 @@ public:
|
|
|
314
309
|
}
|
|
315
310
|
std::unique_ptr<Handler> handleNotifyAlertChanged(NotifyAlertChangedFn fn) override
|
|
316
311
|
{
|
|
317
|
-
return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn)
|
|
312
|
+
return MakeHandler(::uiInterface.NotifyAlertChanged_connect([fn](const uint256 &hash, ChangeType status) {
|
|
313
|
+
fn(hash, status);
|
|
314
|
+
}));
|
|
318
315
|
}
|
|
319
316
|
std::unique_ptr<Handler> handleBannedListChanged(BannedListChangedFn fn) override
|
|
320
317
|
{
|
|
@@ -452,8 +449,8 @@ public:
|
|
|
452
449
|
class ChainImpl : public Chain
|
|
453
450
|
{
|
|
454
451
|
private:
|
|
455
|
-
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
|
|
456
452
|
public:
|
|
453
|
+
ChainstateManager& chainman() override { return *Assert(m_node.chainman); }
|
|
457
454
|
explicit ChainImpl(NodeContext& node) : m_node(node) {}
|
|
458
455
|
std::optional<int> getHeight() override
|
|
459
456
|
{
|
|
@@ -550,7 +547,7 @@ public:
|
|
|
550
547
|
bool hasBlocks(const uint256& block_hash, int min_height, std::optional<int> max_height) override
|
|
551
548
|
{
|
|
552
549
|
// hasBlocks returns true if all ancestors of block_hash in specified
|
|
553
|
-
// range have block data
|
|
550
|
+
// range have block data, false if any ancestors in
|
|
554
551
|
// specified range are missing data.
|
|
555
552
|
//
|
|
556
553
|
// For simplicity and robustness, min_height and max_height are only
|
|
@@ -566,12 +563,14 @@ public:
|
|
|
566
563
|
}
|
|
567
564
|
return false;
|
|
568
565
|
}
|
|
566
|
+
/*
|
|
569
567
|
RBFTransactionState isRBFOptIn(const CTransaction& tx) override
|
|
570
568
|
{
|
|
571
569
|
if (!m_node.mempool) return IsRBFOptInEmptyMempool(tx);
|
|
572
570
|
LOCK(m_node.mempool->cs);
|
|
573
571
|
return IsRBFOptIn(tx, *m_node.mempool);
|
|
574
572
|
}
|
|
573
|
+
*/
|
|
575
574
|
bool isInMempool(const uint256& txid) override
|
|
576
575
|
{
|
|
577
576
|
if (!m_node.mempool) return false;
|
|
@@ -586,11 +585,10 @@ public:
|
|
|
586
585
|
return it && (*it)->GetCountWithDescendants() > 1;
|
|
587
586
|
}
|
|
588
587
|
bool broadcastTransaction(const CTransactionRef& tx,
|
|
589
|
-
const CAmount& max_tx_fee,
|
|
590
588
|
bool relay,
|
|
591
589
|
std::string& err_string) override
|
|
592
590
|
{
|
|
593
|
-
const TransactionError err = BroadcastTransaction(m_node, tx, err_string,
|
|
591
|
+
const TransactionError err = BroadcastTransaction(m_node, tx, err_string, relay, /*wait_callback*/ false);
|
|
594
592
|
// Chain clients only care about failures to accept the tx to the mempool. Disregard non-mempool related failures.
|
|
595
593
|
// Note: this will need to be updated if BroadcastTransactions() is updated to return other non-mempool failures
|
|
596
594
|
// that Chain clients do not need to know about.
|
|
@@ -623,6 +621,7 @@ public:
|
|
|
623
621
|
entry, ancestors, limit_ancestor_count, limit_ancestor_size,
|
|
624
622
|
limit_descendant_count, limit_descendant_size, unused_error_string);
|
|
625
623
|
}
|
|
624
|
+
/*
|
|
626
625
|
CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override
|
|
627
626
|
{
|
|
628
627
|
if (!m_node.fee_estimator) return {};
|
|
@@ -641,11 +640,7 @@ public:
|
|
|
641
640
|
CFeeRate relayMinFee() override { return ::minRelayTxFee; }
|
|
642
641
|
CFeeRate relayIncrementalFee() override { return ::incrementalRelayFee; }
|
|
643
642
|
CFeeRate relayDustFee() override { return ::dustRelayFee; }
|
|
644
|
-
|
|
645
|
-
{
|
|
646
|
-
LOCK(cs_main);
|
|
647
|
-
return node::fHavePruned;
|
|
648
|
-
}
|
|
643
|
+
*/
|
|
649
644
|
bool isReadyToBroadcast() override { return !node::fImporting && !node::fReindex && !isInitialBlockDownload(); }
|
|
650
645
|
bool isInitialBlockDownload() override {
|
|
651
646
|
return chainman().ActiveChainstate().IsInitialBlockDownload();
|
|
@@ -13,34 +13,53 @@
|
|
|
13
13
|
#include <consensus/merkle.h>
|
|
14
14
|
#include <consensus/tx_verify.h>
|
|
15
15
|
#include <consensus/validation.h>
|
|
16
|
-
#include <deploymentstatus.h>
|
|
17
|
-
#include <policy/feerate.h>
|
|
18
16
|
#include <policy/policy.h>
|
|
19
17
|
#include <pow.h>
|
|
20
18
|
#include <primitives/transaction.h>
|
|
19
|
+
#include <rpc/blockchain.h>
|
|
21
20
|
#include <timedata.h>
|
|
21
|
+
#include <rpc/blockchain.h>
|
|
22
22
|
#include <util/moneystr.h>
|
|
23
23
|
#include <util/system.h>
|
|
24
|
+
#include <util/threadnames.h>
|
|
25
|
+
#include <util/translation.h>
|
|
24
26
|
#include <validation.h>
|
|
27
|
+
#include <kernel.h>
|
|
28
|
+
#include <net.h>
|
|
29
|
+
#include <interfaces/chain.h>
|
|
30
|
+
#include <node/context.h>
|
|
31
|
+
#include <node/ui_interface.h>
|
|
32
|
+
#include <util/thread.h>
|
|
33
|
+
#include <validation.h>
|
|
34
|
+
#include <wallet/wallet.h>
|
|
35
|
+
#include <wallet/coincontrol.h>
|
|
36
|
+
#include <warnings.h>
|
|
37
|
+
#include <wallet/spend.h>
|
|
38
|
+
#include <wallet/wallet.h>
|
|
25
39
|
|
|
26
40
|
#include <algorithm>
|
|
27
41
|
#include <utility>
|
|
28
42
|
|
|
43
|
+
#include <boost/thread.hpp>
|
|
44
|
+
|
|
45
|
+
using wallet::CWallet;
|
|
46
|
+
using wallet::COutput;
|
|
47
|
+
using wallet::CCoinControl;
|
|
48
|
+
using wallet::ReserveDestination;
|
|
49
|
+
|
|
50
|
+
int64_t nLastCoinStakeSearchInterval = 0;
|
|
51
|
+
std::thread m_minter_thread;
|
|
52
|
+
|
|
29
53
|
namespace node {
|
|
30
|
-
int64_t UpdateTime(CBlockHeader* pblock
|
|
54
|
+
int64_t UpdateTime(CBlockHeader* pblock)
|
|
31
55
|
{
|
|
32
56
|
int64_t nOldTime = pblock->nTime;
|
|
33
|
-
int64_t nNewTime = std::max(
|
|
57
|
+
int64_t nNewTime = std::max(pblock->GetBlockTime(), GetAdjustedTime());
|
|
34
58
|
|
|
35
59
|
if (nOldTime < nNewTime) {
|
|
36
60
|
pblock->nTime = nNewTime;
|
|
37
61
|
}
|
|
38
62
|
|
|
39
|
-
// Updating time can change work required on testnet:
|
|
40
|
-
if (consensusParams.fPowAllowMinDifficultyBlocks) {
|
|
41
|
-
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
63
|
return nNewTime - nOldTime;
|
|
45
64
|
}
|
|
46
65
|
|
|
@@ -58,7 +77,6 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
|
|
|
58
77
|
|
|
59
78
|
BlockAssembler::Options::Options()
|
|
60
79
|
{
|
|
61
|
-
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
|
|
62
80
|
nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT;
|
|
63
81
|
}
|
|
64
82
|
|
|
@@ -67,7 +85,6 @@ BlockAssembler::BlockAssembler(CChainState& chainstate, const CTxMemPool& mempoo
|
|
|
67
85
|
m_mempool(mempool),
|
|
68
86
|
m_chainstate(chainstate)
|
|
69
87
|
{
|
|
70
|
-
blockMinFeeRate = options.blockMinFeeRate;
|
|
71
88
|
// Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity:
|
|
72
89
|
nBlockMaxWeight = std::max<size_t>(4000, std::min<size_t>(MAX_BLOCK_WEIGHT - 4000, options.nBlockMaxWeight));
|
|
73
90
|
}
|
|
@@ -78,12 +95,6 @@ static BlockAssembler::Options DefaultOptions()
|
|
|
78
95
|
// If -blockmaxweight is not given, limit to DEFAULT_BLOCK_MAX_WEIGHT
|
|
79
96
|
BlockAssembler::Options options;
|
|
80
97
|
options.nBlockMaxWeight = gArgs.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
|
|
81
|
-
if (gArgs.IsArgSet("-blockmintxfee")) {
|
|
82
|
-
std::optional<CAmount> parsed = ParseMoney(gArgs.GetArg("-blockmintxfee", ""));
|
|
83
|
-
options.blockMinFeeRate = CFeeRate{parsed.value_or(DEFAULT_BLOCK_MIN_TX_FEE)};
|
|
84
|
-
} else {
|
|
85
|
-
options.blockMinFeeRate = CFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
|
|
86
|
-
}
|
|
87
98
|
return options;
|
|
88
99
|
}
|
|
89
100
|
|
|
@@ -104,7 +115,8 @@ void BlockAssembler::resetBlock()
|
|
|
104
115
|
nFees = 0;
|
|
105
116
|
}
|
|
106
117
|
|
|
107
|
-
|
|
118
|
+
// peercoin: if pwallet != NULL it will attempt to create coinstake
|
|
119
|
+
std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet, bool* pfPoSCancel, NodeContext* m_node)
|
|
108
120
|
{
|
|
109
121
|
int64_t nTimeStart = GetTimeMicros();
|
|
110
122
|
|
|
@@ -116,25 +128,67 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
|
|
116
128
|
return nullptr;
|
|
117
129
|
}
|
|
118
130
|
CBlock* const pblock = &pblocktemplate->block; // pointer for convenience
|
|
131
|
+
pblock->nTime = GetAdjustedTime();
|
|
132
|
+
|
|
133
|
+
LOCK2(cs_main, m_mempool.cs);
|
|
134
|
+
|
|
135
|
+
CBlockIndex* pindexPrev = m_node->chainman->ActiveChain().Tip();
|
|
136
|
+
assert(pindexPrev != nullptr);
|
|
137
|
+
nHeight = pindexPrev->nHeight + 1;
|
|
138
|
+
|
|
139
|
+
// Create coinbase transaction.
|
|
140
|
+
CMutableTransaction coinbaseTx;
|
|
141
|
+
coinbaseTx.vin.resize(1);
|
|
142
|
+
coinbaseTx.vin[0].prevout.SetNull();
|
|
143
|
+
coinbaseTx.vout.resize(1);
|
|
144
|
+
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
|
|
145
|
+
|
|
146
|
+
if (pwallet == nullptr) {
|
|
147
|
+
pblock->nBits = GetNextTargetRequired(pindexPrev, false, chainparams.GetConsensus());
|
|
148
|
+
coinbaseTx.vout[0].nValue = GetProofOfWorkReward(pblock->nBits, pblock->nTime);
|
|
149
|
+
}
|
|
119
150
|
|
|
120
151
|
// Add dummy coinbase tx as first transaction
|
|
121
152
|
pblock->vtx.emplace_back();
|
|
122
153
|
pblocktemplate->vTxFees.push_back(-1); // updated at end
|
|
123
154
|
pblocktemplate->vTxSigOpsCost.push_back(-1); // updated at end
|
|
124
155
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
156
|
+
// peercoin: if coinstake available add coinstake tx
|
|
157
|
+
static int64_t nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup
|
|
158
|
+
|
|
159
|
+
if (pwallet) // attemp to find a coinstake
|
|
160
|
+
{
|
|
161
|
+
*pfPoSCancel = true;
|
|
162
|
+
pblock->nBits = GetNextTargetRequired(pindexPrev, true, chainparams.GetConsensus());
|
|
163
|
+
CMutableTransaction txCoinStake;
|
|
164
|
+
int64_t nSearchTime = txCoinStake.nTime; // search to current time
|
|
165
|
+
if (nSearchTime > nLastCoinStakeSearchTime)
|
|
166
|
+
{
|
|
167
|
+
if (pwallet->CreateCoinStake(*m_node->chainman, pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake))
|
|
168
|
+
{
|
|
169
|
+
if (txCoinStake.nTime >= std::max(pindexPrev->GetMedianTimePast()+1, pindexPrev->GetBlockTime() - (IsProtocolV09(pindexPrev->GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9)))
|
|
170
|
+
{ // make sure coinstake would meet timestamp protocol
|
|
171
|
+
// as it would be the same as the block timestamp
|
|
172
|
+
coinbaseTx.vout[0].SetEmpty();
|
|
173
|
+
coinbaseTx.nTime = txCoinStake.nTime;
|
|
174
|
+
pblock->vtx.push_back(MakeTransactionRef(CTransaction(txCoinStake)));
|
|
175
|
+
*pfPoSCancel = false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime;
|
|
179
|
+
nLastCoinStakeSearchTime = nSearchTime;
|
|
180
|
+
}
|
|
181
|
+
if (*pfPoSCancel)
|
|
182
|
+
return nullptr; // peercoin: there is no point to continue if we failed to create coinstake
|
|
183
|
+
pblock->nFlags = CBlockIndex::BLOCK_PROOF_OF_STAKE;
|
|
184
|
+
}
|
|
129
185
|
|
|
130
|
-
pblock->nVersion = g_versionbitscache.ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
|
|
131
186
|
// -regtest only: allow overriding block.nVersion with
|
|
132
187
|
// -blockversion=N to test forking scenarios
|
|
133
188
|
if (chainparams.MineBlocksOnDemand()) {
|
|
134
189
|
pblock->nVersion = gArgs.GetIntArg("-blockversion", pblock->nVersion);
|
|
135
190
|
}
|
|
136
191
|
|
|
137
|
-
pblock->nTime = GetAdjustedTime();
|
|
138
192
|
m_lock_time_cutoff = pindexPrev->GetMedianTimePast();
|
|
139
193
|
|
|
140
194
|
// Decide whether to include witness transactions
|
|
@@ -146,40 +200,38 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
|
|
|
146
200
|
// not activated.
|
|
147
201
|
// TODO: replace this with a call to main to assess validity of a mempool
|
|
148
202
|
// transaction (which in most cases can be a no-op).
|
|
149
|
-
fIncludeWitness =
|
|
203
|
+
fIncludeWitness = IsBTC16BIPsEnabled(pindexPrev->nTime);
|
|
150
204
|
|
|
151
205
|
int nPackagesSelected = 0;
|
|
152
206
|
int nDescendantsUpdated = 0;
|
|
153
|
-
addPackageTxs(nPackagesSelected, nDescendantsUpdated);
|
|
207
|
+
addPackageTxs(nPackagesSelected, nDescendantsUpdated, pblock->nTime);
|
|
154
208
|
|
|
155
209
|
int64_t nTime1 = GetTimeMicros();
|
|
156
210
|
|
|
157
211
|
m_last_block_num_txs = nBlockTx;
|
|
158
212
|
m_last_block_weight = nBlockWeight;
|
|
159
213
|
|
|
160
|
-
// Create coinbase transaction.
|
|
161
|
-
CMutableTransaction coinbaseTx;
|
|
162
|
-
coinbaseTx.vin.resize(1);
|
|
163
|
-
coinbaseTx.vin[0].prevout.SetNull();
|
|
164
|
-
coinbaseTx.vout.resize(1);
|
|
165
|
-
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
|
|
166
|
-
coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
|
|
167
214
|
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
|
|
168
215
|
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
|
|
169
|
-
|
|
216
|
+
if (fIncludeWitness)
|
|
217
|
+
pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus());
|
|
170
218
|
pblocktemplate->vTxFees[0] = -nFees;
|
|
171
219
|
|
|
172
220
|
LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
|
|
173
221
|
|
|
174
222
|
// Fill in header
|
|
175
223
|
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
|
|
176
|
-
|
|
177
|
-
|
|
224
|
+
if (pblock->IsProofOfStake())
|
|
225
|
+
pblock->nTime = pblock->vtx[1]->nTime; //same as coinstake timestamp
|
|
226
|
+
pblock->nTime = std::max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime());
|
|
227
|
+
pblock->nTime = std::max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - (IsProtocolV09(pindexPrev->GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9));
|
|
228
|
+
if (pblock->IsProofOfWork())
|
|
229
|
+
UpdateTime(pblock);
|
|
178
230
|
pblock->nNonce = 0;
|
|
179
231
|
pblocktemplate->vTxSigOpsCost[0] = WITNESS_SCALE_FACTOR * GetLegacySigOpCount(*pblock->vtx[0]);
|
|
180
232
|
|
|
181
233
|
BlockValidationState state;
|
|
182
|
-
if (!TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
|
|
234
|
+
if (pwallet && !TestBlockValidity(state, chainparams, m_chainstate, *pblock, pindexPrev, false, false)) {
|
|
183
235
|
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, state.ToString()));
|
|
184
236
|
}
|
|
185
237
|
int64_t nTime2 = GetTimeMicros();
|
|
@@ -217,7 +269,7 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost
|
|
|
217
269
|
// - transaction finality (locktime)
|
|
218
270
|
// - premature witness (in case segwit transactions are added to mempool before
|
|
219
271
|
// segwit activation)
|
|
220
|
-
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) const
|
|
272
|
+
bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package, uint32_t nTime) const
|
|
221
273
|
{
|
|
222
274
|
for (CTxMemPool::txiter it : package) {
|
|
223
275
|
if (!IsFinalTx(it->GetTx(), nHeight, m_lock_time_cutoff)) {
|
|
@@ -225,6 +277,10 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa
|
|
|
225
277
|
}
|
|
226
278
|
if (!fIncludeWitness && it->GetTx().HasWitness()) {
|
|
227
279
|
return false;
|
|
280
|
+
|
|
281
|
+
// peercoin: timestamp limit
|
|
282
|
+
if (it->GetTx().nTime > GetAdjustedTime() || (nTime && it->GetTx().nTime > nTime))
|
|
283
|
+
return false;
|
|
228
284
|
}
|
|
229
285
|
}
|
|
230
286
|
return true;
|
|
@@ -243,8 +299,8 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
|
|
|
243
299
|
|
|
244
300
|
bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
|
|
245
301
|
if (fPrintPriority) {
|
|
246
|
-
LogPrintf("fee
|
|
247
|
-
|
|
302
|
+
LogPrintf("fee %d satoshi txid %s\n",
|
|
303
|
+
iter->GetModifiedFee(),
|
|
248
304
|
iter->GetTx().GetHash().ToString());
|
|
249
305
|
}
|
|
250
306
|
}
|
|
@@ -317,7 +373,7 @@ void BlockAssembler::SortForBlock(const CTxMemPool::setEntries& package, std::ve
|
|
|
317
373
|
// Each time through the loop, we compare the best transaction in
|
|
318
374
|
// mapModifiedTxs with the next transaction in the mempool to decide what
|
|
319
375
|
// transaction package to work on next.
|
|
320
|
-
void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated)
|
|
376
|
+
void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated, uint32_t nTime)
|
|
321
377
|
{
|
|
322
378
|
AssertLockHeld(m_mempool.cs);
|
|
323
379
|
|
|
@@ -379,19 +435,12 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
|
|
379
435
|
assert(!inBlock.count(iter));
|
|
380
436
|
|
|
381
437
|
uint64_t packageSize = iter->GetSizeWithAncestors();
|
|
382
|
-
CAmount packageFees = iter->GetModFeesWithAncestors();
|
|
383
438
|
int64_t packageSigOpsCost = iter->GetSigOpCostWithAncestors();
|
|
384
439
|
if (fUsingModified) {
|
|
385
440
|
packageSize = modit->nSizeWithAncestors;
|
|
386
|
-
packageFees = modit->nModFeesWithAncestors;
|
|
387
441
|
packageSigOpsCost = modit->nSigOpCostWithAncestors;
|
|
388
442
|
}
|
|
389
443
|
|
|
390
|
-
if (packageFees < blockMinFeeRate.GetFee(packageSize)) {
|
|
391
|
-
// Everything else we might consider has a lower fee rate
|
|
392
|
-
return;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
444
|
if (!TestPackage(packageSize, packageSigOpsCost)) {
|
|
396
445
|
if (fUsingModified) {
|
|
397
446
|
// Since we always look at the best entry in mapModifiedTx,
|
|
@@ -420,7 +469,7 @@ void BlockAssembler::addPackageTxs(int& nPackagesSelected, int& nDescendantsUpda
|
|
|
420
469
|
ancestors.insert(iter);
|
|
421
470
|
|
|
422
471
|
// Test if all tx's are Final
|
|
423
|
-
if (!TestPackageTransactions(ancestors)) {
|
|
472
|
+
if (!TestPackageTransactions(ancestors,nTime)) {
|
|
424
473
|
if (fUsingModified) {
|
|
425
474
|
mapModifiedTx.get<ancestor_score>().erase(modit);
|
|
426
475
|
failedTx.insert(iter);
|
|
@@ -465,4 +514,208 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
|
|
|
465
514
|
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
|
|
466
515
|
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
|
|
467
516
|
}
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams, NodeContext& m_node)
|
|
520
|
+
{
|
|
521
|
+
LogPrintf("%s\n", pblock->ToString());
|
|
522
|
+
LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0]->vout[0].nValue));
|
|
523
|
+
|
|
524
|
+
// Found a solution
|
|
525
|
+
{
|
|
526
|
+
LOCK(cs_main);
|
|
527
|
+
if (pblock->hashPrevBlock != m_node.chainman->ActiveChain().Tip()->GetBlockHash())
|
|
528
|
+
return error("PeercoinMiner: generated block is stale");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Process this block the same as if we had received it from another node
|
|
532
|
+
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(*pblock);
|
|
533
|
+
if (!m_node.chainman->ProcessNewBlock(Params(), shared_pblock, true, NULL))
|
|
534
|
+
return error("ProcessNewBlock, block not accepted");
|
|
535
|
+
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
void PoSMiner(std::shared_ptr<CWallet> pwallet, NodeContext& m_node)
|
|
540
|
+
{
|
|
541
|
+
CConnman* connman = m_node.connman.get();
|
|
542
|
+
LogPrintf("CPUMiner started for proof-of-stake\n");
|
|
543
|
+
util::ThreadRename("peercoin-stake-minter");
|
|
544
|
+
|
|
545
|
+
unsigned int nExtraNonce = 0;
|
|
546
|
+
|
|
547
|
+
OutputType output_type = pwallet->m_default_change_type ? *pwallet->m_default_change_type : pwallet->m_default_address_type;
|
|
548
|
+
ReserveDestination reservedest(pwallet.get(), output_type);
|
|
549
|
+
CTxDestination dest;
|
|
550
|
+
// Compute timeout for pos as sqrt(numUTXO)
|
|
551
|
+
unsigned int pos_timio;
|
|
552
|
+
{
|
|
553
|
+
LOCK2(pwallet->cs_wallet, cs_main);
|
|
554
|
+
bilingual_str dest_err;
|
|
555
|
+
if (!reservedest.GetReservedDestination(dest, true, dest_err))
|
|
556
|
+
throw std::runtime_error("Error: Keypool ran out, please call keypoolrefill first.");
|
|
557
|
+
|
|
558
|
+
std::vector<COutput> vCoins;
|
|
559
|
+
CCoinControl coincontrol;
|
|
560
|
+
AvailableCoins(*pwallet, vCoins, &coincontrol);
|
|
561
|
+
pos_timio = 500 + 30 * sqrt(vCoins.size());
|
|
562
|
+
LogPrintf("Set proof-of-stake timeout: %ums for %u UTXOs\n", pos_timio, vCoins.size());
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
std::string strMintMessage = _("Info: Minting suspended due to locked wallet.").translated;
|
|
566
|
+
std::string strMintSyncMessage = _("Info: Minting suspended while synchronizing wallet.").translated;
|
|
567
|
+
std::string strMintDisabledMessage = _("Info: Minting disabled by 'nominting' option.").translated;
|
|
568
|
+
std::string strMintBlockMessage = _("Info: Minting suspended due to block creation failure.").translated;
|
|
569
|
+
std::string strMintEmpty = "";
|
|
570
|
+
if (!gArgs.GetBoolArg("-minting", true) || !gArgs.GetBoolArg("-staking", true))
|
|
571
|
+
{
|
|
572
|
+
strMintWarning = strMintDisabledMessage;
|
|
573
|
+
LogPrintf("proof-of-stake minter disabled\n");
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
try {
|
|
578
|
+
bool fNeedToClear = false;
|
|
579
|
+
while (true) {
|
|
580
|
+
while (pwallet->IsLocked()) {
|
|
581
|
+
if (strMintWarning != strMintMessage) {
|
|
582
|
+
strMintWarning = strMintMessage;
|
|
583
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED);
|
|
584
|
+
}
|
|
585
|
+
fNeedToClear = true;
|
|
586
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(3)))
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (Params().MiningRequiresPeers()) {
|
|
591
|
+
// Busy-wait for the network to come online so we don't waste time mining
|
|
592
|
+
// on an obsolete chain. In regtest mode we expect to fly solo.
|
|
593
|
+
while(connman == nullptr || connman->GetNodeCount(ConnectionDirection::Both) == 0 || m_node.chainman->ActiveChainstate().IsInitialBlockDownload()) {
|
|
594
|
+
while(connman == nullptr) {UninterruptibleSleep(1s);}
|
|
595
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(10)))
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
while (GuessVerificationProgress(Params().TxData(), m_node.chainman->ActiveChain().Tip()) < 0.996)
|
|
601
|
+
{
|
|
602
|
+
LogPrintf("Minter thread sleeps while sync at %f\n", GuessVerificationProgress(Params().TxData(), m_node.chainman->ActiveChain().Tip()));
|
|
603
|
+
if (strMintWarning != strMintSyncMessage) {
|
|
604
|
+
strMintWarning = strMintSyncMessage;
|
|
605
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED);
|
|
606
|
+
}
|
|
607
|
+
fNeedToClear = true;
|
|
608
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(10)))
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (fNeedToClear) {
|
|
612
|
+
strMintWarning = strMintEmpty;
|
|
613
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED);
|
|
614
|
+
fNeedToClear = false;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
//
|
|
618
|
+
// Create new block
|
|
619
|
+
//
|
|
620
|
+
CBlockIndex* pindexPrev = m_node.chainman->ActiveChain().Tip();
|
|
621
|
+
bool fPoSCancel = false;
|
|
622
|
+
CScript scriptPubKey = GetScriptForDestination(dest);
|
|
623
|
+
CBlock *pblock;
|
|
624
|
+
std::unique_ptr<CBlockTemplate> pblocktemplate;
|
|
625
|
+
|
|
626
|
+
{
|
|
627
|
+
LOCK2(pwallet->cs_wallet, cs_main);
|
|
628
|
+
try {
|
|
629
|
+
pblocktemplate = BlockAssembler(m_node.chainman->ActiveChainstate(), *m_node.mempool, Params()).CreateNewBlock(scriptPubKey, pwallet.get(), &fPoSCancel, &m_node);
|
|
630
|
+
}
|
|
631
|
+
catch (const std::runtime_error &e)
|
|
632
|
+
{
|
|
633
|
+
LogPrintf("PeercoinMiner runtime error: %s\n", e.what());
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (!pblocktemplate.get())
|
|
639
|
+
{
|
|
640
|
+
if (fPoSCancel == true)
|
|
641
|
+
{
|
|
642
|
+
if (!connman->interruptNet.sleep_for(std::chrono::milliseconds(pos_timio)))
|
|
643
|
+
return;
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
strMintWarning = strMintBlockMessage;
|
|
647
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED);
|
|
648
|
+
LogPrintf("Error in PeercoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
|
|
649
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(10)))
|
|
650
|
+
return;
|
|
651
|
+
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
pblock = &pblocktemplate->block;
|
|
655
|
+
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
|
|
656
|
+
|
|
657
|
+
// peercoin: if proof-of-stake block found then process block
|
|
658
|
+
if (pblock->IsProofOfStake())
|
|
659
|
+
{
|
|
660
|
+
{
|
|
661
|
+
LOCK2(pwallet->cs_wallet, cs_main);
|
|
662
|
+
if (!SignBlock(*pblock, *pwallet))
|
|
663
|
+
{
|
|
664
|
+
LogPrintf("PoSMiner(): failed to sign PoS block");
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
LogPrintf("CPUMiner : proof-of-stake block found %s\n", pblock->GetHash().ToString());
|
|
669
|
+
try {
|
|
670
|
+
ProcessBlockFound(pblock, Params(), m_node);
|
|
671
|
+
}
|
|
672
|
+
catch (const std::runtime_error &e)
|
|
673
|
+
{
|
|
674
|
+
LogPrintf("PeercoinMiner runtime error: %s\n", e.what());
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
reservedest.KeepDestination();
|
|
678
|
+
// Rest for ~3 minutes after successful block to preserve close quick
|
|
679
|
+
if (!connman->interruptNet.sleep_for(std::chrono::seconds(60 + GetRand(4))))
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (!connman->interruptNet.sleep_for(std::chrono::milliseconds(pos_timio)))
|
|
683
|
+
return;
|
|
684
|
+
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
catch (::boost::thread_interrupted)
|
|
689
|
+
{
|
|
690
|
+
LogPrintf("PeercoinMiner terminated\n");
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
catch (const std::runtime_error &e)
|
|
694
|
+
{
|
|
695
|
+
LogPrintf("PeercoinMiner runtime error: %s\n", e.what());
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// peercoin: stake minter thread
|
|
701
|
+
void static ThreadStakeMinter(std::shared_ptr<CWallet> pwallet, NodeContext& m_node)
|
|
702
|
+
{
|
|
703
|
+
LogPrintf("ThreadStakeMinter started\n");
|
|
704
|
+
try
|
|
705
|
+
{
|
|
706
|
+
PoSMiner(pwallet, m_node);
|
|
707
|
+
}
|
|
708
|
+
catch (std::exception& e) {
|
|
709
|
+
PrintExceptionContinue(&e, "ThreadStakeMinter()");
|
|
710
|
+
} catch (...) {
|
|
711
|
+
PrintExceptionContinue(NULL, "ThreadStakeMinter()");
|
|
712
|
+
}
|
|
713
|
+
LogPrintf("ThreadStakeMinter exiting\n");
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// peercoin: stake minter
|
|
717
|
+
void MintStake(std::shared_ptr<CWallet> pwallet, NodeContext& m_node)
|
|
718
|
+
{
|
|
719
|
+
m_minter_thread = std::thread([&] { util::TraceThread("minter", [&] { ThreadStakeMinter(pwallet, m_node); }); });
|
|
720
|
+
}
|
|
468
721
|
} // namespace node
|
|
@@ -8,15 +8,18 @@
|
|
|
8
8
|
|
|
9
9
|
#include <primitives/block.h>
|
|
10
10
|
#include <txmempool.h>
|
|
11
|
-
|
|
11
|
+
#include <node/context.h>
|
|
12
12
|
#include <memory>
|
|
13
13
|
#include <optional>
|
|
14
14
|
#include <stdint.h>
|
|
15
|
+
#include <wallet/wallet.h>
|
|
15
16
|
|
|
16
17
|
#include <boost/multi_index/ordered_index.hpp>
|
|
17
18
|
#include <boost/multi_index_container.hpp>
|
|
18
19
|
|
|
20
|
+
extern int64_t nLastCoinStakeSearchInterval;
|
|
19
21
|
class ChainstateManager;
|
|
22
|
+
|
|
20
23
|
class CBlockIndex;
|
|
21
24
|
class CChainParams;
|
|
22
25
|
class CScript;
|
|
@@ -134,7 +137,6 @@ private:
|
|
|
134
137
|
// Configuration parameters for the block size
|
|
135
138
|
bool fIncludeWitness;
|
|
136
139
|
unsigned int nBlockMaxWeight;
|
|
137
|
-
CFeeRate blockMinFeeRate;
|
|
138
140
|
|
|
139
141
|
// Information on the current status of the block
|
|
140
142
|
uint64_t nBlockWeight;
|
|
@@ -155,14 +157,14 @@ public:
|
|
|
155
157
|
struct Options {
|
|
156
158
|
Options();
|
|
157
159
|
size_t nBlockMaxWeight;
|
|
158
|
-
CFeeRate blockMinFeeRate;
|
|
159
160
|
};
|
|
160
161
|
|
|
161
162
|
explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params);
|
|
162
163
|
explicit BlockAssembler(CChainState& chainstate, const CTxMemPool& mempool, const CChainParams& params, const Options& options);
|
|
163
164
|
|
|
164
165
|
/** Construct a new block template with coinbase to scriptPubKeyIn */
|
|
165
|
-
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
|
|
166
|
+
std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn, CWallet* pwallet=nullptr, bool* pfPoSCancel=nullptr, NodeContext* m_node=nullptr);
|
|
167
|
+
//std::unique_ptr<CBlockTemplate> CreateNewBlock(const CScript& scriptPubKeyIn);
|
|
166
168
|
|
|
167
169
|
inline static std::optional<int64_t> m_last_block_num_txs{};
|
|
168
170
|
inline static std::optional<int64_t> m_last_block_weight{};
|
|
@@ -178,7 +180,7 @@ private:
|
|
|
178
180
|
/** Add transactions based on feerate including unconfirmed ancestors
|
|
179
181
|
* Increments nPackagesSelected / nDescendantsUpdated with corresponding
|
|
180
182
|
* statistics from the package selection (for logging statistics). */
|
|
181
|
-
void addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
183
|
+
void addPackageTxs(int& nPackagesSelected, int& nDescendantsUpdated, uint32_t nTime) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
182
184
|
|
|
183
185
|
// helper functions for addPackageTxs()
|
|
184
186
|
/** Remove confirmed (inBlock) entries from given set */
|
|
@@ -189,7 +191,7 @@ private:
|
|
|
189
191
|
* locktime, premature-witness, serialized size (if necessary)
|
|
190
192
|
* These checks should always succeed, and they're here
|
|
191
193
|
* only as an extra check in case of suboptimal node configuration */
|
|
192
|
-
bool TestPackageTransactions(const CTxMemPool::setEntries& package) const;
|
|
194
|
+
bool TestPackageTransactions(const CTxMemPool::setEntries& package, uint32_t nTime) const;
|
|
193
195
|
/** Return true if given transaction from mapTx has already been evaluated,
|
|
194
196
|
* or if the transaction's cached data in mapTx is incorrect. */
|
|
195
197
|
bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set& mapModifiedTx, CTxMemPool::setEntries& failedTx) EXCLUSIVE_LOCKS_REQUIRED(m_mempool.cs);
|
|
@@ -203,7 +205,13 @@ private:
|
|
|
203
205
|
|
|
204
206
|
/** Modify the extranonce in a block */
|
|
205
207
|
void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
|
206
|
-
int64_t UpdateTime(CBlockHeader* pblock
|
|
208
|
+
int64_t UpdateTime(CBlockHeader* pblock);
|
|
209
|
+
|
|
210
|
+
namespace boost {
|
|
211
|
+
class thread_group;
|
|
212
|
+
} // namespace boost
|
|
213
|
+
|
|
214
|
+
void MintStake(std::shared_ptr<CWallet> pwallet, NodeContext& m_node);
|
|
207
215
|
|
|
208
216
|
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
|
|
209
217
|
void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
#include <coins.h>
|
|
6
6
|
#include <consensus/amount.h>
|
|
7
7
|
#include <consensus/tx_verify.h>
|
|
8
|
+
#include <kernel.h>
|
|
8
9
|
#include <node/psbt.h>
|
|
9
10
|
#include <policy/policy.h>
|
|
10
11
|
#include <policy/settings.h>
|
|
12
|
+
#include <timedata.h>
|
|
11
13
|
#include <tinyformat.h>
|
|
12
14
|
|
|
13
15
|
#include <numeric>
|
|
@@ -131,7 +133,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
|
|
|
131
133
|
mtx.vin[i].scriptSig = input.final_script_sig;
|
|
132
134
|
mtx.vin[i].scriptWitness = input.final_script_witness;
|
|
133
135
|
newcoin.nHeight = 1;
|
|
134
|
-
view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true);
|
|
136
|
+
view.AddCoin(psbtx.tx->vin[i].prevout, std::move(newcoin), true, true);
|
|
135
137
|
}
|
|
136
138
|
}
|
|
137
139
|
|
|
@@ -139,9 +141,6 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
|
|
|
139
141
|
CTransaction ctx = CTransaction(mtx);
|
|
140
142
|
size_t size = GetVirtualTransactionSize(ctx, GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS));
|
|
141
143
|
result.estimated_vsize = size;
|
|
142
|
-
// Estimate fee rate
|
|
143
|
-
CFeeRate feerate(fee, size);
|
|
144
|
-
result.estimated_feerate = feerate;
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
}
|
|
@@ -29,7 +29,6 @@ struct PSBTInputAnalysis {
|
|
|
29
29
|
*/
|
|
30
30
|
struct PSBTAnalysis {
|
|
31
31
|
std::optional<size_t> estimated_vsize; //!< Estimated weight of the transaction
|
|
32
|
-
std::optional<CFeeRate> estimated_feerate; //!< Estimated feerate (fee / weight) of the transaction
|
|
33
32
|
std::optional<CAmount> fee; //!< Amount of fee being paid by the transaction
|
|
34
33
|
std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
|
|
35
34
|
PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
|
|
@@ -38,7 +37,6 @@ struct PSBTAnalysis {
|
|
|
38
37
|
void SetInvalid(std::string err_msg)
|
|
39
38
|
{
|
|
40
39
|
estimated_vsize = std::nullopt;
|
|
41
|
-
estimated_feerate = std::nullopt;
|
|
42
40
|
fee = std::nullopt;
|
|
43
41
|
inputs.clear();
|
|
44
42
|
next = PSBTRole::CREATOR;
|
|
@@ -30,7 +30,7 @@ static TransactionError HandleATMPError(const TxValidationState& state, std::str
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string,
|
|
33
|
+
TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef tx, std::string& err_string, bool relay, bool wait_callback)
|
|
34
34
|
{
|
|
35
35
|
// BroadcastTransaction can be called by either sendrawtransaction RPC or the wallet.
|
|
36
36
|
// chainman, mempool and peerman are initialized before the RPC server and wallet are started
|
|
@@ -68,16 +68,6 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
|
|
|
68
68
|
wtxid = mempool_tx->GetWitnessHash();
|
|
69
69
|
} else {
|
|
70
70
|
// Transaction is not already in the mempool.
|
|
71
|
-
if (max_tx_fee > 0) {
|
|
72
|
-
// First, call ATMP with test_accept and check the fee. If ATMP
|
|
73
|
-
// fails here, return error immediately.
|
|
74
|
-
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ true);
|
|
75
|
-
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
|
76
|
-
return HandleATMPError(result.m_state, err_string);
|
|
77
|
-
} else if (result.m_base_fees.value() > max_tx_fee) {
|
|
78
|
-
return TransactionError::MAX_FEE_EXCEEDED;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
71
|
// Try to submit the transaction to the mempool.
|
|
82
72
|
const MempoolAcceptResult result = node.chainman->ProcessTransaction(tx, /*test_accept=*/ false);
|
|
83
73
|
if (result.m_result_type != MempoolAcceptResult::ResultType::VALID) {
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
#define BITCOIN_NODE_TRANSACTION_H
|
|
7
7
|
|
|
8
8
|
#include <attributes.h>
|
|
9
|
-
#include <policy/feerate.h>
|
|
10
9
|
#include <primitives/transaction.h>
|
|
11
10
|
#include <util/error.h>
|
|
12
11
|
|
|
@@ -19,13 +18,6 @@ struct Params;
|
|
|
19
18
|
namespace node {
|
|
20
19
|
struct NodeContext;
|
|
21
20
|
|
|
22
|
-
/** Maximum fee rate for sendrawtransaction and testmempoolaccept RPC calls.
|
|
23
|
-
* Also used by the GUI when broadcasting a completed PSBT.
|
|
24
|
-
* By default, a transaction with a fee rate higher than this will be rejected
|
|
25
|
-
* by these RPCs and the GUI. This can be overridden with the maxfeerate argument.
|
|
26
|
-
*/
|
|
27
|
-
static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
|
|
28
|
-
|
|
29
21
|
/**
|
|
30
22
|
* Submit a transaction to the mempool and (optionally) relay it to all P2P peers.
|
|
31
23
|
*
|
|
@@ -38,12 +30,11 @@ static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
|
|
|
38
30
|
* @param[in] node reference to node context
|
|
39
31
|
* @param[in] tx the transaction to broadcast
|
|
40
32
|
* @param[out] err_string reference to std::string to fill with error string if available
|
|
41
|
-
* @param[in] max_tx_fee reject txs with fees higher than this (if 0, accept any fee)
|
|
42
33
|
* @param[in] relay flag if both mempool insertion and p2p relay are requested
|
|
43
34
|
* @param[in] wait_callback wait until callbacks have been processed to avoid stale result due to a sequentially RPC.
|
|
44
35
|
* return error
|
|
45
36
|
*/
|
|
46
|
-
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string,
|
|
37
|
+
[[nodiscard]] TransactionError BroadcastTransaction(NodeContext& node, CTransactionRef tx, std::string& err_string, bool relay, bool wait_callback);
|
|
47
38
|
|
|
48
39
|
/**
|
|
49
40
|
* Return transaction with a given hash.
|
|
@@ -50,7 +50,7 @@ void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_s
|
|
|
50
50
|
void CClientUIInterface::InitWallet() { return g_ui_signals.InitWallet(); }
|
|
51
51
|
void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); }
|
|
52
52
|
void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); }
|
|
53
|
-
void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); }
|
|
53
|
+
void CClientUIInterface::NotifyAlertChanged(const uint256 &hash, ChangeType status) { return g_ui_signals.NotifyAlertChanged(hash, status); }
|
|
54
54
|
void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); }
|
|
55
55
|
void CClientUIInterface::NotifyBlockTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(s, i); }
|
|
56
56
|
void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(s, i); }
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
#include <functional>
|
|
10
10
|
#include <memory>
|
|
11
11
|
#include <string>
|
|
12
|
+
#include <util/ui_change_type.h>
|
|
12
13
|
|
|
13
14
|
class CBlockIndex;
|
|
15
|
+
class uint256;
|
|
14
16
|
enum class SynchronizationState;
|
|
15
17
|
struct bilingual_str;
|
|
16
18
|
|
|
@@ -93,7 +95,7 @@ public:
|
|
|
93
95
|
/**
|
|
94
96
|
* Status bar alerts changed.
|
|
95
97
|
*/
|
|
96
|
-
ADD_SIGNALS_DECL_WRAPPER(NotifyAlertChanged, void, );
|
|
98
|
+
ADD_SIGNALS_DECL_WRAPPER(NotifyAlertChanged, void, const uint256 &hash, ChangeType status);
|
|
97
99
|
|
|
98
100
|
/**
|
|
99
101
|
* Show progress e.g. for verifychain.
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
|
|
6
|
-
#include <policy/feerate.h>
|
|
7
|
-
|
|
8
|
-
#include <tinyformat.h>
|
|
9
|
-
|
|
10
|
-
#include <cmath>
|
|
11
|
-
|
|
12
|
-
CFeeRate::CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes)
|
|
13
|
-
{
|
|
14
|
-
const int64_t nSize{num_bytes};
|
|
15
|
-
|
|
16
|
-
if (nSize > 0) {
|
|
17
|
-
nSatoshisPerK = nFeePaid * 1000 / nSize;
|
|
18
|
-
} else {
|
|
19
|
-
nSatoshisPerK = 0;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
CAmount CFeeRate::GetFee(uint32_t num_bytes) const
|
|
24
|
-
{
|
|
25
|
-
const int64_t nSize{num_bytes};
|
|
26
|
-
|
|
27
|
-
// Be explicit that we're converting from a double to int64_t (CAmount) here.
|
|
28
|
-
// We've previously had issues with the silent double->int64_t conversion.
|
|
29
|
-
CAmount nFee{static_cast<CAmount>(std::ceil(nSatoshisPerK * nSize / 1000.0))};
|
|
30
|
-
|
|
31
|
-
if (nFee == 0 && nSize != 0) {
|
|
32
|
-
if (nSatoshisPerK > 0) nFee = CAmount(1);
|
|
33
|
-
if (nSatoshisPerK < 0) nFee = CAmount(-1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return nFee;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
std::string CFeeRate::ToString(const FeeEstimateMode& fee_estimate_mode) const
|
|
40
|
-
{
|
|
41
|
-
switch (fee_estimate_mode) {
|
|
42
|
-
case FeeEstimateMode::SAT_VB: return strprintf("%d.%03d %s/vB", nSatoshisPerK / 1000, nSatoshisPerK % 1000, CURRENCY_ATOM);
|
|
43
|
-
default: return strprintf("%d.%08d %s/kvB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
|
|
6
|
-
#ifndef BITCOIN_POLICY_FEERATE_H
|
|
7
|
-
#define BITCOIN_POLICY_FEERATE_H
|
|
8
|
-
|
|
9
|
-
#include <consensus/amount.h>
|
|
10
|
-
#include <serialize.h>
|
|
11
|
-
|
|
12
|
-
#include <string>
|
|
13
|
-
|
|
14
|
-
const std::string CURRENCY_UNIT = "BTC"; // One formatted unit
|
|
15
|
-
const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit
|
|
16
|
-
|
|
17
|
-
/* Used to determine type of fee estimation requested */
|
|
18
|
-
enum class FeeEstimateMode {
|
|
19
|
-
UNSET, //!< Use default settings based on other criteria
|
|
20
|
-
ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
|
|
21
|
-
CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
|
|
22
|
-
BTC_KVB, //!< Use BTC/kvB fee rate unit
|
|
23
|
-
SAT_VB, //!< Use sat/vB fee rate unit
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Fee rate in satoshis per kilovirtualbyte: CAmount / kvB
|
|
28
|
-
*/
|
|
29
|
-
class CFeeRate
|
|
30
|
-
{
|
|
31
|
-
private:
|
|
32
|
-
/** Fee rate in sat/kvB (satoshis per 1000 virtualbytes) */
|
|
33
|
-
CAmount nSatoshisPerK;
|
|
34
|
-
|
|
35
|
-
public:
|
|
36
|
-
/** Fee rate of 0 satoshis per kvB */
|
|
37
|
-
CFeeRate() : nSatoshisPerK(0) { }
|
|
38
|
-
template<typename I>
|
|
39
|
-
explicit CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) {
|
|
40
|
-
// We've previously had bugs creep in from silent double->int conversion...
|
|
41
|
-
static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Construct a fee rate from a fee in satoshis and a vsize in vB.
|
|
46
|
-
*
|
|
47
|
-
* param@[in] nFeePaid The fee paid by a transaction, in satoshis
|
|
48
|
-
* param@[in] num_bytes The vsize of a transaction, in vbytes
|
|
49
|
-
*/
|
|
50
|
-
CFeeRate(const CAmount& nFeePaid, uint32_t num_bytes);
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Return the fee in satoshis for the given vsize in vbytes.
|
|
54
|
-
* If the calculated fee would have fractional satoshis, then the
|
|
55
|
-
* returned fee will always be rounded up to the nearest satoshi.
|
|
56
|
-
*/
|
|
57
|
-
CAmount GetFee(uint32_t num_bytes) const;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Return the fee in satoshis for a vsize of 1000 vbytes
|
|
61
|
-
*/
|
|
62
|
-
CAmount GetFeePerK() const { return GetFee(1000); }
|
|
63
|
-
friend bool operator<(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK < b.nSatoshisPerK; }
|
|
64
|
-
friend bool operator>(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK > b.nSatoshisPerK; }
|
|
65
|
-
friend bool operator==(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK == b.nSatoshisPerK; }
|
|
66
|
-
friend bool operator<=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK <= b.nSatoshisPerK; }
|
|
67
|
-
friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
|
|
68
|
-
friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; }
|
|
69
|
-
CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
|
|
70
|
-
std::string ToString(const FeeEstimateMode& fee_estimate_mode = FeeEstimateMode::BTC_KVB) const;
|
|
71
|
-
|
|
72
|
-
SERIALIZE_METHODS(CFeeRate, obj) { READWRITE(obj.nSatoshisPerK); }
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
#endif // BITCOIN_POLICY_FEERATE_H
|
|
@@ -1,1017 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
|
|
6
|
-
#include <policy/fees.h>
|
|
7
|
-
|
|
8
|
-
#include <clientversion.h>
|
|
9
|
-
#include <fs.h>
|
|
10
|
-
#include <logging.h>
|
|
11
|
-
#include <streams.h>
|
|
12
|
-
#include <txmempool.h>
|
|
13
|
-
#include <util/serfloat.h>
|
|
14
|
-
#include <util/system.h>
|
|
15
|
-
|
|
16
|
-
static const char* FEE_ESTIMATES_FILENAME = "fee_estimates.dat";
|
|
17
|
-
|
|
18
|
-
static constexpr double INF_FEERATE = 1e99;
|
|
19
|
-
|
|
20
|
-
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon)
|
|
21
|
-
{
|
|
22
|
-
switch (horizon) {
|
|
23
|
-
case FeeEstimateHorizon::SHORT_HALFLIFE: return "short";
|
|
24
|
-
case FeeEstimateHorizon::MED_HALFLIFE: return "medium";
|
|
25
|
-
case FeeEstimateHorizon::LONG_HALFLIFE: return "long";
|
|
26
|
-
} // no default case, so the compiler can warn about missing cases
|
|
27
|
-
assert(false);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
namespace {
|
|
31
|
-
|
|
32
|
-
struct EncodedDoubleFormatter
|
|
33
|
-
{
|
|
34
|
-
template<typename Stream> void Ser(Stream &s, double v)
|
|
35
|
-
{
|
|
36
|
-
s << EncodeDouble(v);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
template<typename Stream> void Unser(Stream& s, double& v)
|
|
40
|
-
{
|
|
41
|
-
uint64_t encoded;
|
|
42
|
-
s >> encoded;
|
|
43
|
-
v = DecodeDouble(encoded);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
} // namespace
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* We will instantiate an instance of this class to track transactions that were
|
|
51
|
-
* included in a block. We will lump transactions into a bucket according to their
|
|
52
|
-
* approximate feerate and then track how long it took for those txs to be included in a block
|
|
53
|
-
*
|
|
54
|
-
* The tracking of unconfirmed (mempool) transactions is completely independent of the
|
|
55
|
-
* historical tracking of transactions that have been confirmed in a block.
|
|
56
|
-
*/
|
|
57
|
-
class TxConfirmStats
|
|
58
|
-
{
|
|
59
|
-
private:
|
|
60
|
-
//Define the buckets we will group transactions into
|
|
61
|
-
const std::vector<double>& buckets; // The upper-bound of the range for the bucket (inclusive)
|
|
62
|
-
const std::map<double, unsigned int>& bucketMap; // Map of bucket upper-bound to index into all vectors by bucket
|
|
63
|
-
|
|
64
|
-
// For each bucket X:
|
|
65
|
-
// Count the total # of txs in each bucket
|
|
66
|
-
// Track the historical moving average of this total over blocks
|
|
67
|
-
std::vector<double> txCtAvg;
|
|
68
|
-
|
|
69
|
-
// Count the total # of txs confirmed within Y blocks in each bucket
|
|
70
|
-
// Track the historical moving average of these totals over blocks
|
|
71
|
-
std::vector<std::vector<double>> confAvg; // confAvg[Y][X]
|
|
72
|
-
|
|
73
|
-
// Track moving avg of txs which have been evicted from the mempool
|
|
74
|
-
// after failing to be confirmed within Y blocks
|
|
75
|
-
std::vector<std::vector<double>> failAvg; // failAvg[Y][X]
|
|
76
|
-
|
|
77
|
-
// Sum the total feerate of all tx's in each bucket
|
|
78
|
-
// Track the historical moving average of this total over blocks
|
|
79
|
-
std::vector<double> m_feerate_avg;
|
|
80
|
-
|
|
81
|
-
// Combine the conf counts with tx counts to calculate the confirmation % for each Y,X
|
|
82
|
-
// Combine the total value with the tx counts to calculate the avg feerate per bucket
|
|
83
|
-
|
|
84
|
-
double decay;
|
|
85
|
-
|
|
86
|
-
// Resolution (# of blocks) with which confirmations are tracked
|
|
87
|
-
unsigned int scale;
|
|
88
|
-
|
|
89
|
-
// Mempool counts of outstanding transactions
|
|
90
|
-
// For each bucket X, track the number of transactions in the mempool
|
|
91
|
-
// that are unconfirmed for each possible confirmation value Y
|
|
92
|
-
std::vector<std::vector<int> > unconfTxs; //unconfTxs[Y][X]
|
|
93
|
-
// transactions still unconfirmed after GetMaxConfirms for each bucket
|
|
94
|
-
std::vector<int> oldUnconfTxs;
|
|
95
|
-
|
|
96
|
-
void resizeInMemoryCounters(size_t newbuckets);
|
|
97
|
-
|
|
98
|
-
public:
|
|
99
|
-
/**
|
|
100
|
-
* Create new TxConfirmStats. This is called by BlockPolicyEstimator's
|
|
101
|
-
* constructor with default values.
|
|
102
|
-
* @param defaultBuckets contains the upper limits for the bucket boundaries
|
|
103
|
-
* @param maxPeriods max number of periods to track
|
|
104
|
-
* @param decay how much to decay the historical moving average per block
|
|
105
|
-
*/
|
|
106
|
-
TxConfirmStats(const std::vector<double>& defaultBuckets, const std::map<double, unsigned int>& defaultBucketMap,
|
|
107
|
-
unsigned int maxPeriods, double decay, unsigned int scale);
|
|
108
|
-
|
|
109
|
-
/** Roll the circular buffer for unconfirmed txs*/
|
|
110
|
-
void ClearCurrent(unsigned int nBlockHeight);
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Record a new transaction data point in the current block stats
|
|
114
|
-
* @param blocksToConfirm the number of blocks it took this transaction to confirm
|
|
115
|
-
* @param val the feerate of the transaction
|
|
116
|
-
* @warning blocksToConfirm is 1-based and has to be >= 1
|
|
117
|
-
*/
|
|
118
|
-
void Record(int blocksToConfirm, double val);
|
|
119
|
-
|
|
120
|
-
/** Record a new transaction entering the mempool*/
|
|
121
|
-
unsigned int NewTx(unsigned int nBlockHeight, double val);
|
|
122
|
-
|
|
123
|
-
/** Remove a transaction from mempool tracking stats*/
|
|
124
|
-
void removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight,
|
|
125
|
-
unsigned int bucketIndex, bool inBlock);
|
|
126
|
-
|
|
127
|
-
/** Update our estimates by decaying our historical moving average and updating
|
|
128
|
-
with the data gathered from the current block */
|
|
129
|
-
void UpdateMovingAverages();
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Calculate a feerate estimate. Find the lowest value bucket (or range of buckets
|
|
133
|
-
* to make sure we have enough data points) whose transactions still have sufficient likelihood
|
|
134
|
-
* of being confirmed within the target number of confirmations
|
|
135
|
-
* @param confTarget target number of confirmations
|
|
136
|
-
* @param sufficientTxVal required average number of transactions per block in a bucket range
|
|
137
|
-
* @param minSuccess the success probability we require
|
|
138
|
-
* @param nBlockHeight the current block height
|
|
139
|
-
*/
|
|
140
|
-
double EstimateMedianVal(int confTarget, double sufficientTxVal,
|
|
141
|
-
double minSuccess, unsigned int nBlockHeight,
|
|
142
|
-
EstimationResult *result = nullptr) const;
|
|
143
|
-
|
|
144
|
-
/** Return the max number of confirms we're tracking */
|
|
145
|
-
unsigned int GetMaxConfirms() const { return scale * confAvg.size(); }
|
|
146
|
-
|
|
147
|
-
/** Write state of estimation data to a file*/
|
|
148
|
-
void Write(CAutoFile& fileout) const;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Read saved state of estimation data from a file and replace all internal data structures and
|
|
152
|
-
* variables with this state.
|
|
153
|
-
*/
|
|
154
|
-
void Read(CAutoFile& filein, int nFileVersion, size_t numBuckets);
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
TxConfirmStats::TxConfirmStats(const std::vector<double>& defaultBuckets,
|
|
159
|
-
const std::map<double, unsigned int>& defaultBucketMap,
|
|
160
|
-
unsigned int maxPeriods, double _decay, unsigned int _scale)
|
|
161
|
-
: buckets(defaultBuckets), bucketMap(defaultBucketMap), decay(_decay), scale(_scale)
|
|
162
|
-
{
|
|
163
|
-
assert(_scale != 0 && "_scale must be non-zero");
|
|
164
|
-
confAvg.resize(maxPeriods);
|
|
165
|
-
failAvg.resize(maxPeriods);
|
|
166
|
-
for (unsigned int i = 0; i < maxPeriods; i++) {
|
|
167
|
-
confAvg[i].resize(buckets.size());
|
|
168
|
-
failAvg[i].resize(buckets.size());
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
txCtAvg.resize(buckets.size());
|
|
172
|
-
m_feerate_avg.resize(buckets.size());
|
|
173
|
-
|
|
174
|
-
resizeInMemoryCounters(buckets.size());
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
void TxConfirmStats::resizeInMemoryCounters(size_t newbuckets) {
|
|
178
|
-
// newbuckets must be passed in because the buckets referred to during Read have not been updated yet.
|
|
179
|
-
unconfTxs.resize(GetMaxConfirms());
|
|
180
|
-
for (unsigned int i = 0; i < unconfTxs.size(); i++) {
|
|
181
|
-
unconfTxs[i].resize(newbuckets);
|
|
182
|
-
}
|
|
183
|
-
oldUnconfTxs.resize(newbuckets);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Roll the unconfirmed txs circular buffer
|
|
187
|
-
void TxConfirmStats::ClearCurrent(unsigned int nBlockHeight)
|
|
188
|
-
{
|
|
189
|
-
for (unsigned int j = 0; j < buckets.size(); j++) {
|
|
190
|
-
oldUnconfTxs[j] += unconfTxs[nBlockHeight % unconfTxs.size()][j];
|
|
191
|
-
unconfTxs[nBlockHeight%unconfTxs.size()][j] = 0;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
void TxConfirmStats::Record(int blocksToConfirm, double feerate)
|
|
197
|
-
{
|
|
198
|
-
// blocksToConfirm is 1-based
|
|
199
|
-
if (blocksToConfirm < 1)
|
|
200
|
-
return;
|
|
201
|
-
int periodsToConfirm = (blocksToConfirm + scale - 1) / scale;
|
|
202
|
-
unsigned int bucketindex = bucketMap.lower_bound(feerate)->second;
|
|
203
|
-
for (size_t i = periodsToConfirm; i <= confAvg.size(); i++) {
|
|
204
|
-
confAvg[i - 1][bucketindex]++;
|
|
205
|
-
}
|
|
206
|
-
txCtAvg[bucketindex]++;
|
|
207
|
-
m_feerate_avg[bucketindex] += feerate;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
void TxConfirmStats::UpdateMovingAverages()
|
|
211
|
-
{
|
|
212
|
-
assert(confAvg.size() == failAvg.size());
|
|
213
|
-
for (unsigned int j = 0; j < buckets.size(); j++) {
|
|
214
|
-
for (unsigned int i = 0; i < confAvg.size(); i++) {
|
|
215
|
-
confAvg[i][j] *= decay;
|
|
216
|
-
failAvg[i][j] *= decay;
|
|
217
|
-
}
|
|
218
|
-
m_feerate_avg[j] *= decay;
|
|
219
|
-
txCtAvg[j] *= decay;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// returns -1 on error conditions
|
|
224
|
-
double TxConfirmStats::EstimateMedianVal(int confTarget, double sufficientTxVal,
|
|
225
|
-
double successBreakPoint, unsigned int nBlockHeight,
|
|
226
|
-
EstimationResult *result) const
|
|
227
|
-
{
|
|
228
|
-
// Counters for a bucket (or range of buckets)
|
|
229
|
-
double nConf = 0; // Number of tx's confirmed within the confTarget
|
|
230
|
-
double totalNum = 0; // Total number of tx's that were ever confirmed
|
|
231
|
-
int extraNum = 0; // Number of tx's still in mempool for confTarget or longer
|
|
232
|
-
double failNum = 0; // Number of tx's that were never confirmed but removed from the mempool after confTarget
|
|
233
|
-
const int periodTarget = (confTarget + scale - 1) / scale;
|
|
234
|
-
const int maxbucketindex = buckets.size() - 1;
|
|
235
|
-
|
|
236
|
-
// We'll combine buckets until we have enough samples.
|
|
237
|
-
// The near and far variables will define the range we've combined
|
|
238
|
-
// The best variables are the last range we saw which still had a high
|
|
239
|
-
// enough confirmation rate to count as success.
|
|
240
|
-
// The cur variables are the current range we're counting.
|
|
241
|
-
unsigned int curNearBucket = maxbucketindex;
|
|
242
|
-
unsigned int bestNearBucket = maxbucketindex;
|
|
243
|
-
unsigned int curFarBucket = maxbucketindex;
|
|
244
|
-
unsigned int bestFarBucket = maxbucketindex;
|
|
245
|
-
|
|
246
|
-
bool foundAnswer = false;
|
|
247
|
-
unsigned int bins = unconfTxs.size();
|
|
248
|
-
bool newBucketRange = true;
|
|
249
|
-
bool passing = true;
|
|
250
|
-
EstimatorBucket passBucket;
|
|
251
|
-
EstimatorBucket failBucket;
|
|
252
|
-
|
|
253
|
-
// Start counting from highest feerate transactions
|
|
254
|
-
for (int bucket = maxbucketindex; bucket >= 0; --bucket) {
|
|
255
|
-
if (newBucketRange) {
|
|
256
|
-
curNearBucket = bucket;
|
|
257
|
-
newBucketRange = false;
|
|
258
|
-
}
|
|
259
|
-
curFarBucket = bucket;
|
|
260
|
-
nConf += confAvg[periodTarget - 1][bucket];
|
|
261
|
-
totalNum += txCtAvg[bucket];
|
|
262
|
-
failNum += failAvg[periodTarget - 1][bucket];
|
|
263
|
-
for (unsigned int confct = confTarget; confct < GetMaxConfirms(); confct++)
|
|
264
|
-
extraNum += unconfTxs[(nBlockHeight - confct) % bins][bucket];
|
|
265
|
-
extraNum += oldUnconfTxs[bucket];
|
|
266
|
-
// If we have enough transaction data points in this range of buckets,
|
|
267
|
-
// we can test for success
|
|
268
|
-
// (Only count the confirmed data points, so that each confirmation count
|
|
269
|
-
// will be looking at the same amount of data and same bucket breaks)
|
|
270
|
-
if (totalNum >= sufficientTxVal / (1 - decay)) {
|
|
271
|
-
double curPct = nConf / (totalNum + failNum + extraNum);
|
|
272
|
-
|
|
273
|
-
// Check to see if we are no longer getting confirmed at the success rate
|
|
274
|
-
if (curPct < successBreakPoint) {
|
|
275
|
-
if (passing == true) {
|
|
276
|
-
// First time we hit a failure record the failed bucket
|
|
277
|
-
unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
|
|
278
|
-
unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
|
|
279
|
-
failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;
|
|
280
|
-
failBucket.end = buckets[failMaxBucket];
|
|
281
|
-
failBucket.withinTarget = nConf;
|
|
282
|
-
failBucket.totalConfirmed = totalNum;
|
|
283
|
-
failBucket.inMempool = extraNum;
|
|
284
|
-
failBucket.leftMempool = failNum;
|
|
285
|
-
passing = false;
|
|
286
|
-
}
|
|
287
|
-
continue;
|
|
288
|
-
}
|
|
289
|
-
// Otherwise update the cumulative stats, and the bucket variables
|
|
290
|
-
// and reset the counters
|
|
291
|
-
else {
|
|
292
|
-
failBucket = EstimatorBucket(); // Reset any failed bucket, currently passing
|
|
293
|
-
foundAnswer = true;
|
|
294
|
-
passing = true;
|
|
295
|
-
passBucket.withinTarget = nConf;
|
|
296
|
-
nConf = 0;
|
|
297
|
-
passBucket.totalConfirmed = totalNum;
|
|
298
|
-
totalNum = 0;
|
|
299
|
-
passBucket.inMempool = extraNum;
|
|
300
|
-
passBucket.leftMempool = failNum;
|
|
301
|
-
failNum = 0;
|
|
302
|
-
extraNum = 0;
|
|
303
|
-
bestNearBucket = curNearBucket;
|
|
304
|
-
bestFarBucket = curFarBucket;
|
|
305
|
-
newBucketRange = true;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
double median = -1;
|
|
311
|
-
double txSum = 0;
|
|
312
|
-
|
|
313
|
-
// Calculate the "average" feerate of the best bucket range that met success conditions
|
|
314
|
-
// Find the bucket with the median transaction and then report the average feerate from that bucket
|
|
315
|
-
// This is a compromise between finding the median which we can't since we don't save all tx's
|
|
316
|
-
// and reporting the average which is less accurate
|
|
317
|
-
unsigned int minBucket = std::min(bestNearBucket, bestFarBucket);
|
|
318
|
-
unsigned int maxBucket = std::max(bestNearBucket, bestFarBucket);
|
|
319
|
-
for (unsigned int j = minBucket; j <= maxBucket; j++) {
|
|
320
|
-
txSum += txCtAvg[j];
|
|
321
|
-
}
|
|
322
|
-
if (foundAnswer && txSum != 0) {
|
|
323
|
-
txSum = txSum / 2;
|
|
324
|
-
for (unsigned int j = minBucket; j <= maxBucket; j++) {
|
|
325
|
-
if (txCtAvg[j] < txSum)
|
|
326
|
-
txSum -= txCtAvg[j];
|
|
327
|
-
else { // we're in the right bucket
|
|
328
|
-
median = m_feerate_avg[j] / txCtAvg[j];
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
passBucket.start = minBucket ? buckets[minBucket-1] : 0;
|
|
334
|
-
passBucket.end = buckets[maxBucket];
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// If we were passing until we reached last few buckets with insufficient data, then report those as failed
|
|
338
|
-
if (passing && !newBucketRange) {
|
|
339
|
-
unsigned int failMinBucket = std::min(curNearBucket, curFarBucket);
|
|
340
|
-
unsigned int failMaxBucket = std::max(curNearBucket, curFarBucket);
|
|
341
|
-
failBucket.start = failMinBucket ? buckets[failMinBucket - 1] : 0;
|
|
342
|
-
failBucket.end = buckets[failMaxBucket];
|
|
343
|
-
failBucket.withinTarget = nConf;
|
|
344
|
-
failBucket.totalConfirmed = totalNum;
|
|
345
|
-
failBucket.inMempool = extraNum;
|
|
346
|
-
failBucket.leftMempool = failNum;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
float passed_within_target_perc = 0.0;
|
|
350
|
-
float failed_within_target_perc = 0.0;
|
|
351
|
-
if ((passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool)) {
|
|
352
|
-
passed_within_target_perc = 100 * passBucket.withinTarget / (passBucket.totalConfirmed + passBucket.inMempool + passBucket.leftMempool);
|
|
353
|
-
}
|
|
354
|
-
if ((failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool)) {
|
|
355
|
-
failed_within_target_perc = 100 * failBucket.withinTarget / (failBucket.totalConfirmed + failBucket.inMempool + failBucket.leftMempool);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
LogPrint(BCLog::ESTIMATEFEE, "FeeEst: %d > %.0f%% decay %.5f: feerate: %g from (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n",
|
|
359
|
-
confTarget, 100.0 * successBreakPoint, decay,
|
|
360
|
-
median, passBucket.start, passBucket.end,
|
|
361
|
-
passed_within_target_perc,
|
|
362
|
-
passBucket.withinTarget, passBucket.totalConfirmed, passBucket.inMempool, passBucket.leftMempool,
|
|
363
|
-
failBucket.start, failBucket.end,
|
|
364
|
-
failed_within_target_perc,
|
|
365
|
-
failBucket.withinTarget, failBucket.totalConfirmed, failBucket.inMempool, failBucket.leftMempool);
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
if (result) {
|
|
369
|
-
result->pass = passBucket;
|
|
370
|
-
result->fail = failBucket;
|
|
371
|
-
result->decay = decay;
|
|
372
|
-
result->scale = scale;
|
|
373
|
-
}
|
|
374
|
-
return median;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
void TxConfirmStats::Write(CAutoFile& fileout) const
|
|
378
|
-
{
|
|
379
|
-
fileout << Using<EncodedDoubleFormatter>(decay);
|
|
380
|
-
fileout << scale;
|
|
381
|
-
fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
|
|
382
|
-
fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
|
|
383
|
-
fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
|
|
384
|
-
fileout << Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
void TxConfirmStats::Read(CAutoFile& filein, int nFileVersion, size_t numBuckets)
|
|
388
|
-
{
|
|
389
|
-
// Read data file and do some very basic sanity checking
|
|
390
|
-
// buckets and bucketMap are not updated yet, so don't access them
|
|
391
|
-
// If there is a read failure, we'll just discard this entire object anyway
|
|
392
|
-
size_t maxConfirms, maxPeriods;
|
|
393
|
-
|
|
394
|
-
// The current version will store the decay with each individual TxConfirmStats and also keep a scale factor
|
|
395
|
-
filein >> Using<EncodedDoubleFormatter>(decay);
|
|
396
|
-
if (decay <= 0 || decay >= 1) {
|
|
397
|
-
throw std::runtime_error("Corrupt estimates file. Decay must be between 0 and 1 (non-inclusive)");
|
|
398
|
-
}
|
|
399
|
-
filein >> scale;
|
|
400
|
-
if (scale == 0) {
|
|
401
|
-
throw std::runtime_error("Corrupt estimates file. Scale must be non-zero");
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(m_feerate_avg);
|
|
405
|
-
if (m_feerate_avg.size() != numBuckets) {
|
|
406
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in feerate average bucket count");
|
|
407
|
-
}
|
|
408
|
-
filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(txCtAvg);
|
|
409
|
-
if (txCtAvg.size() != numBuckets) {
|
|
410
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in tx count bucket count");
|
|
411
|
-
}
|
|
412
|
-
filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(confAvg);
|
|
413
|
-
maxPeriods = confAvg.size();
|
|
414
|
-
maxConfirms = scale * maxPeriods;
|
|
415
|
-
|
|
416
|
-
if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) { // one week
|
|
417
|
-
throw std::runtime_error("Corrupt estimates file. Must maintain estimates for between 1 and 1008 (one week) confirms");
|
|
418
|
-
}
|
|
419
|
-
for (unsigned int i = 0; i < maxPeriods; i++) {
|
|
420
|
-
if (confAvg[i].size() != numBuckets) {
|
|
421
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in feerate conf average bucket count");
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
filein >> Using<VectorFormatter<VectorFormatter<EncodedDoubleFormatter>>>(failAvg);
|
|
426
|
-
if (maxPeriods != failAvg.size()) {
|
|
427
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in confirms tracked for failures");
|
|
428
|
-
}
|
|
429
|
-
for (unsigned int i = 0; i < maxPeriods; i++) {
|
|
430
|
-
if (failAvg[i].size() != numBuckets) {
|
|
431
|
-
throw std::runtime_error("Corrupt estimates file. Mismatch in one of failure average bucket counts");
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Resize the current block variables which aren't stored in the data file
|
|
436
|
-
// to match the number of confirms and buckets
|
|
437
|
-
resizeInMemoryCounters(numBuckets);
|
|
438
|
-
|
|
439
|
-
LogPrint(BCLog::ESTIMATEFEE, "Reading estimates: %u buckets counting confirms up to %u blocks\n",
|
|
440
|
-
numBuckets, maxConfirms);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
unsigned int TxConfirmStats::NewTx(unsigned int nBlockHeight, double val)
|
|
444
|
-
{
|
|
445
|
-
unsigned int bucketindex = bucketMap.lower_bound(val)->second;
|
|
446
|
-
unsigned int blockIndex = nBlockHeight % unconfTxs.size();
|
|
447
|
-
unconfTxs[blockIndex][bucketindex]++;
|
|
448
|
-
return bucketindex;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
void TxConfirmStats::removeTx(unsigned int entryHeight, unsigned int nBestSeenHeight, unsigned int bucketindex, bool inBlock)
|
|
452
|
-
{
|
|
453
|
-
//nBestSeenHeight is not updated yet for the new block
|
|
454
|
-
int blocksAgo = nBestSeenHeight - entryHeight;
|
|
455
|
-
if (nBestSeenHeight == 0) // the BlockPolicyEstimator hasn't seen any blocks yet
|
|
456
|
-
blocksAgo = 0;
|
|
457
|
-
if (blocksAgo < 0) {
|
|
458
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, blocks ago is negative for mempool tx\n");
|
|
459
|
-
return; //This can't happen because we call this with our best seen height, no entries can have higher
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (blocksAgo >= (int)unconfTxs.size()) {
|
|
463
|
-
if (oldUnconfTxs[bucketindex] > 0) {
|
|
464
|
-
oldUnconfTxs[bucketindex]--;
|
|
465
|
-
} else {
|
|
466
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from >25 blocks,bucketIndex=%u already\n",
|
|
467
|
-
bucketindex);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
else {
|
|
471
|
-
unsigned int blockIndex = entryHeight % unconfTxs.size();
|
|
472
|
-
if (unconfTxs[blockIndex][bucketindex] > 0) {
|
|
473
|
-
unconfTxs[blockIndex][bucketindex]--;
|
|
474
|
-
} else {
|
|
475
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error, mempool tx removed from blockIndex=%u,bucketIndex=%u already\n",
|
|
476
|
-
blockIndex, bucketindex);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
if (!inBlock && (unsigned int)blocksAgo >= scale) { // Only counts as a failure if not confirmed for entire period
|
|
480
|
-
assert(scale != 0);
|
|
481
|
-
unsigned int periodsAgo = blocksAgo / scale;
|
|
482
|
-
for (size_t i = 0; i < periodsAgo && i < failAvg.size(); i++) {
|
|
483
|
-
failAvg[i][bucketindex]++;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// This function is called from CTxMemPool::removeUnchecked to ensure
|
|
489
|
-
// txs removed from the mempool for any reason are no longer
|
|
490
|
-
// tracked. Txs that were part of a block have already been removed in
|
|
491
|
-
// processBlockTx to ensure they are never double tracked, but it is
|
|
492
|
-
// of no harm to try to remove them again.
|
|
493
|
-
bool CBlockPolicyEstimator::removeTx(uint256 hash, bool inBlock)
|
|
494
|
-
{
|
|
495
|
-
LOCK(m_cs_fee_estimator);
|
|
496
|
-
return _removeTx(hash, inBlock);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
bool CBlockPolicyEstimator::_removeTx(const uint256& hash, bool inBlock)
|
|
500
|
-
{
|
|
501
|
-
AssertLockHeld(m_cs_fee_estimator);
|
|
502
|
-
std::map<uint256, TxStatsInfo>::iterator pos = mapMemPoolTxs.find(hash);
|
|
503
|
-
if (pos != mapMemPoolTxs.end()) {
|
|
504
|
-
feeStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
|
|
505
|
-
shortStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
|
|
506
|
-
longStats->removeTx(pos->second.blockHeight, nBestSeenHeight, pos->second.bucketIndex, inBlock);
|
|
507
|
-
mapMemPoolTxs.erase(hash);
|
|
508
|
-
return true;
|
|
509
|
-
} else {
|
|
510
|
-
return false;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
CBlockPolicyEstimator::CBlockPolicyEstimator()
|
|
515
|
-
: nBestSeenHeight(0), firstRecordedHeight(0), historicalFirst(0), historicalBest(0), trackedTxs(0), untrackedTxs(0)
|
|
516
|
-
{
|
|
517
|
-
static_assert(MIN_BUCKET_FEERATE > 0, "Min feerate must be nonzero");
|
|
518
|
-
size_t bucketIndex = 0;
|
|
519
|
-
|
|
520
|
-
for (double bucketBoundary = MIN_BUCKET_FEERATE; bucketBoundary <= MAX_BUCKET_FEERATE; bucketBoundary *= FEE_SPACING, bucketIndex++) {
|
|
521
|
-
buckets.push_back(bucketBoundary);
|
|
522
|
-
bucketMap[bucketBoundary] = bucketIndex;
|
|
523
|
-
}
|
|
524
|
-
buckets.push_back(INF_FEERATE);
|
|
525
|
-
bucketMap[INF_FEERATE] = bucketIndex;
|
|
526
|
-
assert(bucketMap.size() == buckets.size());
|
|
527
|
-
|
|
528
|
-
feeStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
|
|
529
|
-
shortStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
|
|
530
|
-
longStats = std::unique_ptr<TxConfirmStats>(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
|
|
531
|
-
|
|
532
|
-
// If the fee estimation file is present, read recorded estimations
|
|
533
|
-
fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME;
|
|
534
|
-
CAutoFile est_file(fsbridge::fopen(est_filepath, "rb"), SER_DISK, CLIENT_VERSION);
|
|
535
|
-
if (est_file.IsNull() || !Read(est_file)) {
|
|
536
|
-
LogPrintf("Failed to read fee estimates from %s. Continue anyway.\n", fs::PathToString(est_filepath));
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
CBlockPolicyEstimator::~CBlockPolicyEstimator()
|
|
541
|
-
{
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
void CBlockPolicyEstimator::processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
|
|
545
|
-
{
|
|
546
|
-
LOCK(m_cs_fee_estimator);
|
|
547
|
-
unsigned int txHeight = entry.GetHeight();
|
|
548
|
-
uint256 hash = entry.GetTx().GetHash();
|
|
549
|
-
if (mapMemPoolTxs.count(hash)) {
|
|
550
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error mempool tx %s already being tracked\n",
|
|
551
|
-
hash.ToString());
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (txHeight != nBestSeenHeight) {
|
|
556
|
-
// Ignore side chains and re-orgs; assuming they are random they don't
|
|
557
|
-
// affect the estimate. We'll potentially double count transactions in 1-block reorgs.
|
|
558
|
-
// Ignore txs if BlockPolicyEstimator is not in sync with ActiveChain().Tip().
|
|
559
|
-
// It will be synced next time a block is processed.
|
|
560
|
-
return;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// Only want to be updating estimates when our blockchain is synced,
|
|
564
|
-
// otherwise we'll miscalculate how many blocks its taking to get included.
|
|
565
|
-
if (!validFeeEstimate) {
|
|
566
|
-
untrackedTxs++;
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
trackedTxs++;
|
|
570
|
-
|
|
571
|
-
// Feerates are stored and reported as BTC-per-kb:
|
|
572
|
-
CFeeRate feeRate(entry.GetFee(), entry.GetTxSize());
|
|
573
|
-
|
|
574
|
-
mapMemPoolTxs[hash].blockHeight = txHeight;
|
|
575
|
-
unsigned int bucketIndex = feeStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
|
|
576
|
-
mapMemPoolTxs[hash].bucketIndex = bucketIndex;
|
|
577
|
-
unsigned int bucketIndex2 = shortStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
|
|
578
|
-
assert(bucketIndex == bucketIndex2);
|
|
579
|
-
unsigned int bucketIndex3 = longStats->NewTx(txHeight, (double)feeRate.GetFeePerK());
|
|
580
|
-
assert(bucketIndex == bucketIndex3);
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
bool CBlockPolicyEstimator::processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry)
|
|
584
|
-
{
|
|
585
|
-
AssertLockHeld(m_cs_fee_estimator);
|
|
586
|
-
if (!_removeTx(entry->GetTx().GetHash(), true)) {
|
|
587
|
-
// This transaction wasn't being tracked for fee estimation
|
|
588
|
-
return false;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
// How many blocks did it take for miners to include this transaction?
|
|
592
|
-
// blocksToConfirm is 1-based, so a transaction included in the earliest
|
|
593
|
-
// possible block has confirmation count of 1
|
|
594
|
-
int blocksToConfirm = nBlockHeight - entry->GetHeight();
|
|
595
|
-
if (blocksToConfirm <= 0) {
|
|
596
|
-
// This can't happen because we don't process transactions from a block with a height
|
|
597
|
-
// lower than our greatest seen height
|
|
598
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy error Transaction had negative blocksToConfirm\n");
|
|
599
|
-
return false;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
// Feerates are stored and reported as BTC-per-kb:
|
|
603
|
-
CFeeRate feeRate(entry->GetFee(), entry->GetTxSize());
|
|
604
|
-
|
|
605
|
-
feeStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
|
|
606
|
-
shortStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
|
|
607
|
-
longStats->Record(blocksToConfirm, (double)feeRate.GetFeePerK());
|
|
608
|
-
return true;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
void CBlockPolicyEstimator::processBlock(unsigned int nBlockHeight,
|
|
612
|
-
std::vector<const CTxMemPoolEntry*>& entries)
|
|
613
|
-
{
|
|
614
|
-
LOCK(m_cs_fee_estimator);
|
|
615
|
-
if (nBlockHeight <= nBestSeenHeight) {
|
|
616
|
-
// Ignore side chains and re-orgs; assuming they are random
|
|
617
|
-
// they don't affect the estimate.
|
|
618
|
-
// And if an attacker can re-org the chain at will, then
|
|
619
|
-
// you've got much bigger problems than "attacker can influence
|
|
620
|
-
// transaction fees."
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Must update nBestSeenHeight in sync with ClearCurrent so that
|
|
625
|
-
// calls to removeTx (via processBlockTx) correctly calculate age
|
|
626
|
-
// of unconfirmed txs to remove from tracking.
|
|
627
|
-
nBestSeenHeight = nBlockHeight;
|
|
628
|
-
|
|
629
|
-
// Update unconfirmed circular buffer
|
|
630
|
-
feeStats->ClearCurrent(nBlockHeight);
|
|
631
|
-
shortStats->ClearCurrent(nBlockHeight);
|
|
632
|
-
longStats->ClearCurrent(nBlockHeight);
|
|
633
|
-
|
|
634
|
-
// Decay all exponential averages
|
|
635
|
-
feeStats->UpdateMovingAverages();
|
|
636
|
-
shortStats->UpdateMovingAverages();
|
|
637
|
-
longStats->UpdateMovingAverages();
|
|
638
|
-
|
|
639
|
-
unsigned int countedTxs = 0;
|
|
640
|
-
// Update averages with data points from current block
|
|
641
|
-
for (const auto& entry : entries) {
|
|
642
|
-
if (processBlockTx(nBlockHeight, entry))
|
|
643
|
-
countedTxs++;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
if (firstRecordedHeight == 0 && countedTxs > 0) {
|
|
647
|
-
firstRecordedHeight = nBestSeenHeight;
|
|
648
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy first recorded height %u\n", firstRecordedHeight);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
LogPrint(BCLog::ESTIMATEFEE, "Blockpolicy estimates updated by %u of %u block txs, since last block %u of %u tracked, mempool map size %u, max target %u from %s\n",
|
|
653
|
-
countedTxs, entries.size(), trackedTxs, trackedTxs + untrackedTxs, mapMemPoolTxs.size(),
|
|
654
|
-
MaxUsableEstimate(), HistoricalBlockSpan() > BlockSpan() ? "historical" : "current");
|
|
655
|
-
|
|
656
|
-
trackedTxs = 0;
|
|
657
|
-
untrackedTxs = 0;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
CFeeRate CBlockPolicyEstimator::estimateFee(int confTarget) const
|
|
661
|
-
{
|
|
662
|
-
// It's not possible to get reasonable estimates for confTarget of 1
|
|
663
|
-
if (confTarget <= 1)
|
|
664
|
-
return CFeeRate(0);
|
|
665
|
-
|
|
666
|
-
return estimateRawFee(confTarget, DOUBLE_SUCCESS_PCT, FeeEstimateHorizon::MED_HALFLIFE);
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon, EstimationResult* result) const
|
|
670
|
-
{
|
|
671
|
-
TxConfirmStats* stats = nullptr;
|
|
672
|
-
double sufficientTxs = SUFFICIENT_FEETXS;
|
|
673
|
-
switch (horizon) {
|
|
674
|
-
case FeeEstimateHorizon::SHORT_HALFLIFE: {
|
|
675
|
-
stats = shortStats.get();
|
|
676
|
-
sufficientTxs = SUFFICIENT_TXS_SHORT;
|
|
677
|
-
break;
|
|
678
|
-
}
|
|
679
|
-
case FeeEstimateHorizon::MED_HALFLIFE: {
|
|
680
|
-
stats = feeStats.get();
|
|
681
|
-
break;
|
|
682
|
-
}
|
|
683
|
-
case FeeEstimateHorizon::LONG_HALFLIFE: {
|
|
684
|
-
stats = longStats.get();
|
|
685
|
-
break;
|
|
686
|
-
}
|
|
687
|
-
} // no default case, so the compiler can warn about missing cases
|
|
688
|
-
assert(stats);
|
|
689
|
-
|
|
690
|
-
LOCK(m_cs_fee_estimator);
|
|
691
|
-
// Return failure if trying to analyze a target we're not tracking
|
|
692
|
-
if (confTarget <= 0 || (unsigned int)confTarget > stats->GetMaxConfirms())
|
|
693
|
-
return CFeeRate(0);
|
|
694
|
-
if (successThreshold > 1)
|
|
695
|
-
return CFeeRate(0);
|
|
696
|
-
|
|
697
|
-
double median = stats->EstimateMedianVal(confTarget, sufficientTxs, successThreshold, nBestSeenHeight, result);
|
|
698
|
-
|
|
699
|
-
if (median < 0)
|
|
700
|
-
return CFeeRate(0);
|
|
701
|
-
|
|
702
|
-
return CFeeRate(llround(median));
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const
|
|
706
|
-
{
|
|
707
|
-
LOCK(m_cs_fee_estimator);
|
|
708
|
-
switch (horizon) {
|
|
709
|
-
case FeeEstimateHorizon::SHORT_HALFLIFE: {
|
|
710
|
-
return shortStats->GetMaxConfirms();
|
|
711
|
-
}
|
|
712
|
-
case FeeEstimateHorizon::MED_HALFLIFE: {
|
|
713
|
-
return feeStats->GetMaxConfirms();
|
|
714
|
-
}
|
|
715
|
-
case FeeEstimateHorizon::LONG_HALFLIFE: {
|
|
716
|
-
return longStats->GetMaxConfirms();
|
|
717
|
-
}
|
|
718
|
-
} // no default case, so the compiler can warn about missing cases
|
|
719
|
-
assert(false);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
unsigned int CBlockPolicyEstimator::BlockSpan() const
|
|
723
|
-
{
|
|
724
|
-
if (firstRecordedHeight == 0) return 0;
|
|
725
|
-
assert(nBestSeenHeight >= firstRecordedHeight);
|
|
726
|
-
|
|
727
|
-
return nBestSeenHeight - firstRecordedHeight;
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
unsigned int CBlockPolicyEstimator::HistoricalBlockSpan() const
|
|
731
|
-
{
|
|
732
|
-
if (historicalFirst == 0) return 0;
|
|
733
|
-
assert(historicalBest >= historicalFirst);
|
|
734
|
-
|
|
735
|
-
if (nBestSeenHeight - historicalBest > OLDEST_ESTIMATE_HISTORY) return 0;
|
|
736
|
-
|
|
737
|
-
return historicalBest - historicalFirst;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
unsigned int CBlockPolicyEstimator::MaxUsableEstimate() const
|
|
741
|
-
{
|
|
742
|
-
// Block spans are divided by 2 to make sure there are enough potential failing data points for the estimate
|
|
743
|
-
return std::min(longStats->GetMaxConfirms(), std::max(BlockSpan(), HistoricalBlockSpan()) / 2);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
/** Return a fee estimate at the required successThreshold from the shortest
|
|
747
|
-
* time horizon which tracks confirmations up to the desired target. If
|
|
748
|
-
* checkShorterHorizon is requested, also allow short time horizon estimates
|
|
749
|
-
* for a lower target to reduce the given answer */
|
|
750
|
-
double CBlockPolicyEstimator::estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const
|
|
751
|
-
{
|
|
752
|
-
double estimate = -1;
|
|
753
|
-
if (confTarget >= 1 && confTarget <= longStats->GetMaxConfirms()) {
|
|
754
|
-
// Find estimate from shortest time horizon possible
|
|
755
|
-
if (confTarget <= shortStats->GetMaxConfirms()) { // short horizon
|
|
756
|
-
estimate = shortStats->EstimateMedianVal(confTarget, SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, result);
|
|
757
|
-
}
|
|
758
|
-
else if (confTarget <= feeStats->GetMaxConfirms()) { // medium horizon
|
|
759
|
-
estimate = feeStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
|
|
760
|
-
}
|
|
761
|
-
else { // long horizon
|
|
762
|
-
estimate = longStats->EstimateMedianVal(confTarget, SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, result);
|
|
763
|
-
}
|
|
764
|
-
if (checkShorterHorizon) {
|
|
765
|
-
EstimationResult tempResult;
|
|
766
|
-
// If a lower confTarget from a more recent horizon returns a lower answer use it.
|
|
767
|
-
if (confTarget > feeStats->GetMaxConfirms()) {
|
|
768
|
-
double medMax = feeStats->EstimateMedianVal(feeStats->GetMaxConfirms(), SUFFICIENT_FEETXS, successThreshold, nBestSeenHeight, &tempResult);
|
|
769
|
-
if (medMax > 0 && (estimate == -1 || medMax < estimate)) {
|
|
770
|
-
estimate = medMax;
|
|
771
|
-
if (result) *result = tempResult;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
if (confTarget > shortStats->GetMaxConfirms()) {
|
|
775
|
-
double shortMax = shortStats->EstimateMedianVal(shortStats->GetMaxConfirms(), SUFFICIENT_TXS_SHORT, successThreshold, nBestSeenHeight, &tempResult);
|
|
776
|
-
if (shortMax > 0 && (estimate == -1 || shortMax < estimate)) {
|
|
777
|
-
estimate = shortMax;
|
|
778
|
-
if (result) *result = tempResult;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
return estimate;
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/** Ensure that for a conservative estimate, the DOUBLE_SUCCESS_PCT is also met
|
|
787
|
-
* at 2 * target for any longer time horizons.
|
|
788
|
-
*/
|
|
789
|
-
double CBlockPolicyEstimator::estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const
|
|
790
|
-
{
|
|
791
|
-
double estimate = -1;
|
|
792
|
-
EstimationResult tempResult;
|
|
793
|
-
if (doubleTarget <= shortStats->GetMaxConfirms()) {
|
|
794
|
-
estimate = feeStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, result);
|
|
795
|
-
}
|
|
796
|
-
if (doubleTarget <= feeStats->GetMaxConfirms()) {
|
|
797
|
-
double longEstimate = longStats->EstimateMedianVal(doubleTarget, SUFFICIENT_FEETXS, DOUBLE_SUCCESS_PCT, nBestSeenHeight, &tempResult);
|
|
798
|
-
if (longEstimate > estimate) {
|
|
799
|
-
estimate = longEstimate;
|
|
800
|
-
if (result) *result = tempResult;
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
return estimate;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
/** estimateSmartFee returns the max of the feerates calculated with a 60%
|
|
807
|
-
* threshold required at target / 2, an 85% threshold required at target and a
|
|
808
|
-
* 95% threshold required at 2 * target. Each calculation is performed at the
|
|
809
|
-
* shortest time horizon which tracks the required target. Conservative
|
|
810
|
-
* estimates, however, required the 95% threshold at 2 * target be met for any
|
|
811
|
-
* longer time horizons also.
|
|
812
|
-
*/
|
|
813
|
-
CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
|
|
814
|
-
{
|
|
815
|
-
LOCK(m_cs_fee_estimator);
|
|
816
|
-
|
|
817
|
-
if (feeCalc) {
|
|
818
|
-
feeCalc->desiredTarget = confTarget;
|
|
819
|
-
feeCalc->returnedTarget = confTarget;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
double median = -1;
|
|
823
|
-
EstimationResult tempResult;
|
|
824
|
-
|
|
825
|
-
// Return failure if trying to analyze a target we're not tracking
|
|
826
|
-
if (confTarget <= 0 || (unsigned int)confTarget > longStats->GetMaxConfirms()) {
|
|
827
|
-
return CFeeRate(0); // error condition
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
// It's not possible to get reasonable estimates for confTarget of 1
|
|
831
|
-
if (confTarget == 1) confTarget = 2;
|
|
832
|
-
|
|
833
|
-
unsigned int maxUsableEstimate = MaxUsableEstimate();
|
|
834
|
-
if ((unsigned int)confTarget > maxUsableEstimate) {
|
|
835
|
-
confTarget = maxUsableEstimate;
|
|
836
|
-
}
|
|
837
|
-
if (feeCalc) feeCalc->returnedTarget = confTarget;
|
|
838
|
-
|
|
839
|
-
if (confTarget <= 1) return CFeeRate(0); // error condition
|
|
840
|
-
|
|
841
|
-
assert(confTarget > 0); //estimateCombinedFee and estimateConservativeFee take unsigned ints
|
|
842
|
-
/** true is passed to estimateCombined fee for target/2 and target so
|
|
843
|
-
* that we check the max confirms for shorter time horizons as well.
|
|
844
|
-
* This is necessary to preserve monotonically increasing estimates.
|
|
845
|
-
* For non-conservative estimates we do the same thing for 2*target, but
|
|
846
|
-
* for conservative estimates we want to skip these shorter horizons
|
|
847
|
-
* checks for 2*target because we are taking the max over all time
|
|
848
|
-
* horizons so we already have monotonically increasing estimates and
|
|
849
|
-
* the purpose of conservative estimates is not to let short term
|
|
850
|
-
* fluctuations lower our estimates by too much.
|
|
851
|
-
*/
|
|
852
|
-
double halfEst = estimateCombinedFee(confTarget/2, HALF_SUCCESS_PCT, true, &tempResult);
|
|
853
|
-
if (feeCalc) {
|
|
854
|
-
feeCalc->est = tempResult;
|
|
855
|
-
feeCalc->reason = FeeReason::HALF_ESTIMATE;
|
|
856
|
-
}
|
|
857
|
-
median = halfEst;
|
|
858
|
-
double actualEst = estimateCombinedFee(confTarget, SUCCESS_PCT, true, &tempResult);
|
|
859
|
-
if (actualEst > median) {
|
|
860
|
-
median = actualEst;
|
|
861
|
-
if (feeCalc) {
|
|
862
|
-
feeCalc->est = tempResult;
|
|
863
|
-
feeCalc->reason = FeeReason::FULL_ESTIMATE;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
|
-
double doubleEst = estimateCombinedFee(2 * confTarget, DOUBLE_SUCCESS_PCT, !conservative, &tempResult);
|
|
867
|
-
if (doubleEst > median) {
|
|
868
|
-
median = doubleEst;
|
|
869
|
-
if (feeCalc) {
|
|
870
|
-
feeCalc->est = tempResult;
|
|
871
|
-
feeCalc->reason = FeeReason::DOUBLE_ESTIMATE;
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
if (conservative || median == -1) {
|
|
876
|
-
double consEst = estimateConservativeFee(2 * confTarget, &tempResult);
|
|
877
|
-
if (consEst > median) {
|
|
878
|
-
median = consEst;
|
|
879
|
-
if (feeCalc) {
|
|
880
|
-
feeCalc->est = tempResult;
|
|
881
|
-
feeCalc->reason = FeeReason::CONSERVATIVE;
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
if (median < 0) return CFeeRate(0); // error condition
|
|
887
|
-
|
|
888
|
-
return CFeeRate(llround(median));
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
void CBlockPolicyEstimator::Flush() {
|
|
892
|
-
FlushUnconfirmed();
|
|
893
|
-
|
|
894
|
-
fs::path est_filepath = gArgs.GetDataDirNet() / FEE_ESTIMATES_FILENAME;
|
|
895
|
-
CAutoFile est_file(fsbridge::fopen(est_filepath, "wb"), SER_DISK, CLIENT_VERSION);
|
|
896
|
-
if (est_file.IsNull() || !Write(est_file)) {
|
|
897
|
-
LogPrintf("Failed to write fee estimates to %s. Continue anyway.\n", fs::PathToString(est_filepath));
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
bool CBlockPolicyEstimator::Write(CAutoFile& fileout) const
|
|
902
|
-
{
|
|
903
|
-
try {
|
|
904
|
-
LOCK(m_cs_fee_estimator);
|
|
905
|
-
fileout << 149900; // version required to read: 0.14.99 or later
|
|
906
|
-
fileout << CLIENT_VERSION; // version that wrote the file
|
|
907
|
-
fileout << nBestSeenHeight;
|
|
908
|
-
if (BlockSpan() > HistoricalBlockSpan()/2) {
|
|
909
|
-
fileout << firstRecordedHeight << nBestSeenHeight;
|
|
910
|
-
}
|
|
911
|
-
else {
|
|
912
|
-
fileout << historicalFirst << historicalBest;
|
|
913
|
-
}
|
|
914
|
-
fileout << Using<VectorFormatter<EncodedDoubleFormatter>>(buckets);
|
|
915
|
-
feeStats->Write(fileout);
|
|
916
|
-
shortStats->Write(fileout);
|
|
917
|
-
longStats->Write(fileout);
|
|
918
|
-
}
|
|
919
|
-
catch (const std::exception&) {
|
|
920
|
-
LogPrintf("CBlockPolicyEstimator::Write(): unable to write policy estimator data (non-fatal)\n");
|
|
921
|
-
return false;
|
|
922
|
-
}
|
|
923
|
-
return true;
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
bool CBlockPolicyEstimator::Read(CAutoFile& filein)
|
|
927
|
-
{
|
|
928
|
-
try {
|
|
929
|
-
LOCK(m_cs_fee_estimator);
|
|
930
|
-
int nVersionRequired, nVersionThatWrote;
|
|
931
|
-
filein >> nVersionRequired >> nVersionThatWrote;
|
|
932
|
-
if (nVersionRequired > CLIENT_VERSION) {
|
|
933
|
-
throw std::runtime_error(strprintf("up-version (%d) fee estimate file", nVersionRequired));
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
// Read fee estimates file into temporary variables so existing data
|
|
937
|
-
// structures aren't corrupted if there is an exception.
|
|
938
|
-
unsigned int nFileBestSeenHeight;
|
|
939
|
-
filein >> nFileBestSeenHeight;
|
|
940
|
-
|
|
941
|
-
if (nVersionRequired < 149900) {
|
|
942
|
-
LogPrintf("%s: incompatible old fee estimation data (non-fatal). Version: %d\n", __func__, nVersionRequired);
|
|
943
|
-
} else { // New format introduced in 149900
|
|
944
|
-
unsigned int nFileHistoricalFirst, nFileHistoricalBest;
|
|
945
|
-
filein >> nFileHistoricalFirst >> nFileHistoricalBest;
|
|
946
|
-
if (nFileHistoricalFirst > nFileHistoricalBest || nFileHistoricalBest > nFileBestSeenHeight) {
|
|
947
|
-
throw std::runtime_error("Corrupt estimates file. Historical block range for estimates is invalid");
|
|
948
|
-
}
|
|
949
|
-
std::vector<double> fileBuckets;
|
|
950
|
-
filein >> Using<VectorFormatter<EncodedDoubleFormatter>>(fileBuckets);
|
|
951
|
-
size_t numBuckets = fileBuckets.size();
|
|
952
|
-
if (numBuckets <= 1 || numBuckets > 1000) {
|
|
953
|
-
throw std::runtime_error("Corrupt estimates file. Must have between 2 and 1000 feerate buckets");
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
std::unique_ptr<TxConfirmStats> fileFeeStats(new TxConfirmStats(buckets, bucketMap, MED_BLOCK_PERIODS, MED_DECAY, MED_SCALE));
|
|
957
|
-
std::unique_ptr<TxConfirmStats> fileShortStats(new TxConfirmStats(buckets, bucketMap, SHORT_BLOCK_PERIODS, SHORT_DECAY, SHORT_SCALE));
|
|
958
|
-
std::unique_ptr<TxConfirmStats> fileLongStats(new TxConfirmStats(buckets, bucketMap, LONG_BLOCK_PERIODS, LONG_DECAY, LONG_SCALE));
|
|
959
|
-
fileFeeStats->Read(filein, nVersionThatWrote, numBuckets);
|
|
960
|
-
fileShortStats->Read(filein, nVersionThatWrote, numBuckets);
|
|
961
|
-
fileLongStats->Read(filein, nVersionThatWrote, numBuckets);
|
|
962
|
-
|
|
963
|
-
// Fee estimates file parsed correctly
|
|
964
|
-
// Copy buckets from file and refresh our bucketmap
|
|
965
|
-
buckets = fileBuckets;
|
|
966
|
-
bucketMap.clear();
|
|
967
|
-
for (unsigned int i = 0; i < buckets.size(); i++) {
|
|
968
|
-
bucketMap[buckets[i]] = i;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
// Destroy old TxConfirmStats and point to new ones that already reference buckets and bucketMap
|
|
972
|
-
feeStats = std::move(fileFeeStats);
|
|
973
|
-
shortStats = std::move(fileShortStats);
|
|
974
|
-
longStats = std::move(fileLongStats);
|
|
975
|
-
|
|
976
|
-
nBestSeenHeight = nFileBestSeenHeight;
|
|
977
|
-
historicalFirst = nFileHistoricalFirst;
|
|
978
|
-
historicalBest = nFileHistoricalBest;
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
catch (const std::exception& e) {
|
|
982
|
-
LogPrintf("CBlockPolicyEstimator::Read(): unable to read policy estimator data (non-fatal): %s\n",e.what());
|
|
983
|
-
return false;
|
|
984
|
-
}
|
|
985
|
-
return true;
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
void CBlockPolicyEstimator::FlushUnconfirmed() {
|
|
989
|
-
int64_t startclear = GetTimeMicros();
|
|
990
|
-
LOCK(m_cs_fee_estimator);
|
|
991
|
-
size_t num_entries = mapMemPoolTxs.size();
|
|
992
|
-
// Remove every entry in mapMemPoolTxs
|
|
993
|
-
while (!mapMemPoolTxs.empty()) {
|
|
994
|
-
auto mi = mapMemPoolTxs.begin();
|
|
995
|
-
_removeTx(mi->first, false); // this calls erase() on mapMemPoolTxs
|
|
996
|
-
}
|
|
997
|
-
int64_t endclear = GetTimeMicros();
|
|
998
|
-
LogPrint(BCLog::ESTIMATEFEE, "Recorded %u unconfirmed txs from mempool in %gs\n", num_entries, (endclear - startclear)*0.000001);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
FeeFilterRounder::FeeFilterRounder(const CFeeRate& minIncrementalFee)
|
|
1002
|
-
{
|
|
1003
|
-
CAmount minFeeLimit = std::max(CAmount(1), minIncrementalFee.GetFeePerK() / 2);
|
|
1004
|
-
feeset.insert(0);
|
|
1005
|
-
for (double bucketBoundary = minFeeLimit; bucketBoundary <= MAX_FILTER_FEERATE; bucketBoundary *= FEE_FILTER_SPACING) {
|
|
1006
|
-
feeset.insert(bucketBoundary);
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
CAmount FeeFilterRounder::round(CAmount currentMinFee)
|
|
1011
|
-
{
|
|
1012
|
-
std::set<double>::iterator it = feeset.lower_bound(currentMinFee);
|
|
1013
|
-
if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) {
|
|
1014
|
-
it--;
|
|
1015
|
-
}
|
|
1016
|
-
return static_cast<CAmount>(*it);
|
|
1017
|
-
}
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
2
|
-
// Copyright (c) 2009-2021 The Bitcoin Core developers
|
|
3
|
-
// Distributed under the MIT software license, see the accompanying
|
|
4
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
5
|
-
#ifndef BITCOIN_POLICY_FEES_H
|
|
6
|
-
#define BITCOIN_POLICY_FEES_H
|
|
7
|
-
|
|
8
|
-
#include <consensus/amount.h>
|
|
9
|
-
#include <policy/feerate.h>
|
|
10
|
-
#include <uint256.h>
|
|
11
|
-
#include <random.h>
|
|
12
|
-
#include <sync.h>
|
|
13
|
-
|
|
14
|
-
#include <array>
|
|
15
|
-
#include <map>
|
|
16
|
-
#include <memory>
|
|
17
|
-
#include <string>
|
|
18
|
-
#include <vector>
|
|
19
|
-
|
|
20
|
-
class CAutoFile;
|
|
21
|
-
class CFeeRate;
|
|
22
|
-
class CTxMemPoolEntry;
|
|
23
|
-
class CTxMemPool;
|
|
24
|
-
class TxConfirmStats;
|
|
25
|
-
|
|
26
|
-
/* Identifier for each of the 3 different TxConfirmStats which will track
|
|
27
|
-
* history over different time horizons. */
|
|
28
|
-
enum class FeeEstimateHorizon {
|
|
29
|
-
SHORT_HALFLIFE,
|
|
30
|
-
MED_HALFLIFE,
|
|
31
|
-
LONG_HALFLIFE,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
static constexpr auto ALL_FEE_ESTIMATE_HORIZONS = std::array{
|
|
35
|
-
FeeEstimateHorizon::SHORT_HALFLIFE,
|
|
36
|
-
FeeEstimateHorizon::MED_HALFLIFE,
|
|
37
|
-
FeeEstimateHorizon::LONG_HALFLIFE,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
std::string StringForFeeEstimateHorizon(FeeEstimateHorizon horizon);
|
|
41
|
-
|
|
42
|
-
/* Enumeration of reason for returned fee estimate */
|
|
43
|
-
enum class FeeReason {
|
|
44
|
-
NONE,
|
|
45
|
-
HALF_ESTIMATE,
|
|
46
|
-
FULL_ESTIMATE,
|
|
47
|
-
DOUBLE_ESTIMATE,
|
|
48
|
-
CONSERVATIVE,
|
|
49
|
-
MEMPOOL_MIN,
|
|
50
|
-
PAYTXFEE,
|
|
51
|
-
FALLBACK,
|
|
52
|
-
REQUIRED,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/* Used to return detailed information about a feerate bucket */
|
|
56
|
-
struct EstimatorBucket
|
|
57
|
-
{
|
|
58
|
-
double start = -1;
|
|
59
|
-
double end = -1;
|
|
60
|
-
double withinTarget = 0;
|
|
61
|
-
double totalConfirmed = 0;
|
|
62
|
-
double inMempool = 0;
|
|
63
|
-
double leftMempool = 0;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/* Used to return detailed information about a fee estimate calculation */
|
|
67
|
-
struct EstimationResult
|
|
68
|
-
{
|
|
69
|
-
EstimatorBucket pass;
|
|
70
|
-
EstimatorBucket fail;
|
|
71
|
-
double decay = 0;
|
|
72
|
-
unsigned int scale = 0;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
struct FeeCalculation
|
|
76
|
-
{
|
|
77
|
-
EstimationResult est;
|
|
78
|
-
FeeReason reason = FeeReason::NONE;
|
|
79
|
-
int desiredTarget = 0;
|
|
80
|
-
int returnedTarget = 0;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
/** \class CBlockPolicyEstimator
|
|
84
|
-
* The BlockPolicyEstimator is used for estimating the feerate needed
|
|
85
|
-
* for a transaction to be included in a block within a certain number of
|
|
86
|
-
* blocks.
|
|
87
|
-
*
|
|
88
|
-
* At a high level the algorithm works by grouping transactions into buckets
|
|
89
|
-
* based on having similar feerates and then tracking how long it
|
|
90
|
-
* takes transactions in the various buckets to be mined. It operates under
|
|
91
|
-
* the assumption that in general transactions of higher feerate will be
|
|
92
|
-
* included in blocks before transactions of lower feerate. So for
|
|
93
|
-
* example if you wanted to know what feerate you should put on a transaction to
|
|
94
|
-
* be included in a block within the next 5 blocks, you would start by looking
|
|
95
|
-
* at the bucket with the highest feerate transactions and verifying that a
|
|
96
|
-
* sufficiently high percentage of them were confirmed within 5 blocks and
|
|
97
|
-
* then you would look at the next highest feerate bucket, and so on, stopping at
|
|
98
|
-
* the last bucket to pass the test. The average feerate of transactions in this
|
|
99
|
-
* bucket will give you an indication of the lowest feerate you can put on a
|
|
100
|
-
* transaction and still have a sufficiently high chance of being confirmed
|
|
101
|
-
* within your desired 5 blocks.
|
|
102
|
-
*
|
|
103
|
-
* Here is a brief description of the implementation:
|
|
104
|
-
* When a transaction enters the mempool, we track the height of the block chain
|
|
105
|
-
* at entry. All further calculations are conducted only on this set of "seen"
|
|
106
|
-
* transactions. Whenever a block comes in, we count the number of transactions
|
|
107
|
-
* in each bucket and the total amount of feerate paid in each bucket. Then we
|
|
108
|
-
* calculate how many blocks Y it took each transaction to be mined. We convert
|
|
109
|
-
* from a number of blocks to a number of periods Y' each encompassing "scale"
|
|
110
|
-
* blocks. This is tracked in 3 different data sets each up to a maximum
|
|
111
|
-
* number of periods. Within each data set we have an array of counters in each
|
|
112
|
-
* feerate bucket and we increment all the counters from Y' up to max periods
|
|
113
|
-
* representing that a tx was successfully confirmed in less than or equal to
|
|
114
|
-
* that many periods. We want to save a history of this information, so at any
|
|
115
|
-
* time we have a counter of the total number of transactions that happened in a
|
|
116
|
-
* given feerate bucket and the total number that were confirmed in each of the
|
|
117
|
-
* periods or less for any bucket. We save this history by keeping an
|
|
118
|
-
* exponentially decaying moving average of each one of these stats. This is
|
|
119
|
-
* done for a different decay in each of the 3 data sets to keep relevant data
|
|
120
|
-
* from different time horizons. Furthermore we also keep track of the number
|
|
121
|
-
* unmined (in mempool or left mempool without being included in a block)
|
|
122
|
-
* transactions in each bucket and for how many blocks they have been
|
|
123
|
-
* outstanding and use both of these numbers to increase the number of transactions
|
|
124
|
-
* we've seen in that feerate bucket when calculating an estimate for any number
|
|
125
|
-
* of confirmations below the number of blocks they've been outstanding.
|
|
126
|
-
*
|
|
127
|
-
* We want to be able to estimate feerates that are needed on tx's to be included in
|
|
128
|
-
* a certain number of blocks. Every time a block is added to the best chain, this class records
|
|
129
|
-
* stats on the transactions included in that block
|
|
130
|
-
*/
|
|
131
|
-
class CBlockPolicyEstimator
|
|
132
|
-
{
|
|
133
|
-
private:
|
|
134
|
-
/** Track confirm delays up to 12 blocks for short horizon */
|
|
135
|
-
static constexpr unsigned int SHORT_BLOCK_PERIODS = 12;
|
|
136
|
-
static constexpr unsigned int SHORT_SCALE = 1;
|
|
137
|
-
/** Track confirm delays up to 48 blocks for medium horizon */
|
|
138
|
-
static constexpr unsigned int MED_BLOCK_PERIODS = 24;
|
|
139
|
-
static constexpr unsigned int MED_SCALE = 2;
|
|
140
|
-
/** Track confirm delays up to 1008 blocks for long horizon */
|
|
141
|
-
static constexpr unsigned int LONG_BLOCK_PERIODS = 42;
|
|
142
|
-
static constexpr unsigned int LONG_SCALE = 24;
|
|
143
|
-
/** Historical estimates that are older than this aren't valid */
|
|
144
|
-
static const unsigned int OLDEST_ESTIMATE_HISTORY = 6 * 1008;
|
|
145
|
-
|
|
146
|
-
/** Decay of .962 is a half-life of 18 blocks or about 3 hours */
|
|
147
|
-
static constexpr double SHORT_DECAY = .962;
|
|
148
|
-
/** Decay of .9952 is a half-life of 144 blocks or about 1 day */
|
|
149
|
-
static constexpr double MED_DECAY = .9952;
|
|
150
|
-
/** Decay of .99931 is a half-life of 1008 blocks or about 1 week */
|
|
151
|
-
static constexpr double LONG_DECAY = .99931;
|
|
152
|
-
|
|
153
|
-
/** Require greater than 60% of X feerate transactions to be confirmed within Y/2 blocks*/
|
|
154
|
-
static constexpr double HALF_SUCCESS_PCT = .6;
|
|
155
|
-
/** Require greater than 85% of X feerate transactions to be confirmed within Y blocks*/
|
|
156
|
-
static constexpr double SUCCESS_PCT = .85;
|
|
157
|
-
/** Require greater than 95% of X feerate transactions to be confirmed within 2 * Y blocks*/
|
|
158
|
-
static constexpr double DOUBLE_SUCCESS_PCT = .95;
|
|
159
|
-
|
|
160
|
-
/** Require an avg of 0.1 tx in the combined feerate bucket per block to have stat significance */
|
|
161
|
-
static constexpr double SUFFICIENT_FEETXS = 0.1;
|
|
162
|
-
/** Require an avg of 0.5 tx when using short decay since there are fewer blocks considered*/
|
|
163
|
-
static constexpr double SUFFICIENT_TXS_SHORT = 0.5;
|
|
164
|
-
|
|
165
|
-
/** Minimum and Maximum values for tracking feerates
|
|
166
|
-
* The MIN_BUCKET_FEERATE should just be set to the lowest reasonable feerate we
|
|
167
|
-
* might ever want to track. Historically this has been 1000 since it was
|
|
168
|
-
* inheriting DEFAULT_MIN_RELAY_TX_FEE and changing it is disruptive as it
|
|
169
|
-
* invalidates old estimates files. So leave it at 1000 unless it becomes
|
|
170
|
-
* necessary to lower it, and then lower it substantially.
|
|
171
|
-
*/
|
|
172
|
-
static constexpr double MIN_BUCKET_FEERATE = 1000;
|
|
173
|
-
static constexpr double MAX_BUCKET_FEERATE = 1e7;
|
|
174
|
-
|
|
175
|
-
/** Spacing of FeeRate buckets
|
|
176
|
-
* We have to lump transactions into buckets based on feerate, but we want to be able
|
|
177
|
-
* to give accurate estimates over a large range of potential feerates
|
|
178
|
-
* Therefore it makes sense to exponentially space the buckets
|
|
179
|
-
*/
|
|
180
|
-
static constexpr double FEE_SPACING = 1.05;
|
|
181
|
-
|
|
182
|
-
public:
|
|
183
|
-
/** Create new BlockPolicyEstimator and initialize stats tracking classes with default values */
|
|
184
|
-
CBlockPolicyEstimator();
|
|
185
|
-
~CBlockPolicyEstimator();
|
|
186
|
-
|
|
187
|
-
/** Process all the transactions that have been included in a block */
|
|
188
|
-
void processBlock(unsigned int nBlockHeight,
|
|
189
|
-
std::vector<const CTxMemPoolEntry*>& entries)
|
|
190
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
191
|
-
|
|
192
|
-
/** Process a transaction accepted to the mempool*/
|
|
193
|
-
void processTransaction(const CTxMemPoolEntry& entry, bool validFeeEstimate)
|
|
194
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
195
|
-
|
|
196
|
-
/** Remove a transaction from the mempool tracking stats*/
|
|
197
|
-
bool removeTx(uint256 hash, bool inBlock)
|
|
198
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
199
|
-
|
|
200
|
-
/** DEPRECATED. Return a feerate estimate */
|
|
201
|
-
CFeeRate estimateFee(int confTarget) const
|
|
202
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
203
|
-
|
|
204
|
-
/** Estimate feerate needed to get be included in a block within confTarget
|
|
205
|
-
* blocks. If no answer can be given at confTarget, return an estimate at
|
|
206
|
-
* the closest target where one can be given. 'conservative' estimates are
|
|
207
|
-
* valid over longer time horizons also.
|
|
208
|
-
*/
|
|
209
|
-
CFeeRate estimateSmartFee(int confTarget, FeeCalculation *feeCalc, bool conservative) const
|
|
210
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
211
|
-
|
|
212
|
-
/** Return a specific fee estimate calculation with a given success
|
|
213
|
-
* threshold and time horizon, and optionally return detailed data about
|
|
214
|
-
* calculation
|
|
215
|
-
*/
|
|
216
|
-
CFeeRate estimateRawFee(int confTarget, double successThreshold, FeeEstimateHorizon horizon,
|
|
217
|
-
EstimationResult* result = nullptr) const
|
|
218
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
219
|
-
|
|
220
|
-
/** Write estimation data to a file */
|
|
221
|
-
bool Write(CAutoFile& fileout) const
|
|
222
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
223
|
-
|
|
224
|
-
/** Read estimation data from a file */
|
|
225
|
-
bool Read(CAutoFile& filein)
|
|
226
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
227
|
-
|
|
228
|
-
/** Empty mempool transactions on shutdown to record failure to confirm for txs still in mempool */
|
|
229
|
-
void FlushUnconfirmed()
|
|
230
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
231
|
-
|
|
232
|
-
/** Calculation of highest target that estimates are tracked for */
|
|
233
|
-
unsigned int HighestTargetTracked(FeeEstimateHorizon horizon) const
|
|
234
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
235
|
-
|
|
236
|
-
/** Drop still unconfirmed transactions and record current estimations, if the fee estimation file is present. */
|
|
237
|
-
void Flush()
|
|
238
|
-
EXCLUSIVE_LOCKS_REQUIRED(!m_cs_fee_estimator);
|
|
239
|
-
|
|
240
|
-
private:
|
|
241
|
-
mutable Mutex m_cs_fee_estimator;
|
|
242
|
-
|
|
243
|
-
unsigned int nBestSeenHeight GUARDED_BY(m_cs_fee_estimator);
|
|
244
|
-
unsigned int firstRecordedHeight GUARDED_BY(m_cs_fee_estimator);
|
|
245
|
-
unsigned int historicalFirst GUARDED_BY(m_cs_fee_estimator);
|
|
246
|
-
unsigned int historicalBest GUARDED_BY(m_cs_fee_estimator);
|
|
247
|
-
|
|
248
|
-
struct TxStatsInfo
|
|
249
|
-
{
|
|
250
|
-
unsigned int blockHeight;
|
|
251
|
-
unsigned int bucketIndex;
|
|
252
|
-
TxStatsInfo() : blockHeight(0), bucketIndex(0) {}
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
// map of txids to information about that transaction
|
|
256
|
-
std::map<uint256, TxStatsInfo> mapMemPoolTxs GUARDED_BY(m_cs_fee_estimator);
|
|
257
|
-
|
|
258
|
-
/** Classes to track historical data on transaction confirmations */
|
|
259
|
-
std::unique_ptr<TxConfirmStats> feeStats PT_GUARDED_BY(m_cs_fee_estimator);
|
|
260
|
-
std::unique_ptr<TxConfirmStats> shortStats PT_GUARDED_BY(m_cs_fee_estimator);
|
|
261
|
-
std::unique_ptr<TxConfirmStats> longStats PT_GUARDED_BY(m_cs_fee_estimator);
|
|
262
|
-
|
|
263
|
-
unsigned int trackedTxs GUARDED_BY(m_cs_fee_estimator);
|
|
264
|
-
unsigned int untrackedTxs GUARDED_BY(m_cs_fee_estimator);
|
|
265
|
-
|
|
266
|
-
std::vector<double> buckets GUARDED_BY(m_cs_fee_estimator); // The upper-bound of the range for the bucket (inclusive)
|
|
267
|
-
std::map<double, unsigned int> bucketMap GUARDED_BY(m_cs_fee_estimator); // Map of bucket upper-bound to index into all vectors by bucket
|
|
268
|
-
|
|
269
|
-
/** Process a transaction confirmed in a block*/
|
|
270
|
-
bool processBlockTx(unsigned int nBlockHeight, const CTxMemPoolEntry* entry) EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
271
|
-
|
|
272
|
-
/** Helper for estimateSmartFee */
|
|
273
|
-
double estimateCombinedFee(unsigned int confTarget, double successThreshold, bool checkShorterHorizon, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
274
|
-
/** Helper for estimateSmartFee */
|
|
275
|
-
double estimateConservativeFee(unsigned int doubleTarget, EstimationResult *result) const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
276
|
-
/** Number of blocks of data recorded while fee estimates have been running */
|
|
277
|
-
unsigned int BlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
278
|
-
/** Number of blocks of recorded fee estimate data represented in saved data file */
|
|
279
|
-
unsigned int HistoricalBlockSpan() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
280
|
-
/** Calculation of highest target that reasonable estimate can be provided for */
|
|
281
|
-
unsigned int MaxUsableEstimate() const EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
282
|
-
|
|
283
|
-
/** A non-thread-safe helper for the removeTx function */
|
|
284
|
-
bool _removeTx(const uint256& hash, bool inBlock)
|
|
285
|
-
EXCLUSIVE_LOCKS_REQUIRED(m_cs_fee_estimator);
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
class FeeFilterRounder
|
|
289
|
-
{
|
|
290
|
-
private:
|
|
291
|
-
static constexpr double MAX_FILTER_FEERATE = 1e7;
|
|
292
|
-
/** FEE_FILTER_SPACING is just used to provide some quantization of fee
|
|
293
|
-
* filter results. Historically it reused FEE_SPACING, but it is completely
|
|
294
|
-
* unrelated, and was made a separate constant so the two concepts are not
|
|
295
|
-
* tied together */
|
|
296
|
-
static constexpr double FEE_FILTER_SPACING = 1.1;
|
|
297
|
-
|
|
298
|
-
public:
|
|
299
|
-
/** Create new FeeFilterRounder */
|
|
300
|
-
explicit FeeFilterRounder(const CFeeRate& minIncrementalFee);
|
|
301
|
-
|
|
302
|
-
/** Quantize a minimum fee for privacy purpose before broadcast. Not thread-safe due to use of FastRandomContext */
|
|
303
|
-
CAmount round(CAmount currentMinFee);
|
|
304
|
-
|
|
305
|
-
private:
|
|
306
|
-
std::set<double> feeset;
|
|
307
|
-
FastRandomContext insecure_rand;
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
#endif // BITCOIN_POLICY_FEES_H
|
|
@@ -9,52 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
#include <consensus/validation.h>
|
|
11
11
|
#include <coins.h>
|
|
12
|
+
#include <kernel.h>
|
|
12
13
|
#include <span.h>
|
|
13
14
|
|
|
14
|
-
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
|
15
|
-
{
|
|
16
|
-
// "Dust" is defined in terms of dustRelayFee,
|
|
17
|
-
// which has units satoshis-per-kilobyte.
|
|
18
|
-
// If you'd pay more in fees than the value of the output
|
|
19
|
-
// to spend something, then we consider it dust.
|
|
20
|
-
// A typical spendable non-segwit txout is 34 bytes big, and will
|
|
21
|
-
// need a CTxIn of at least 148 bytes to spend:
|
|
22
|
-
// so dust is a spendable txout less than
|
|
23
|
-
// 182*dustRelayFee/1000 (in satoshis).
|
|
24
|
-
// 546 satoshis at the default rate of 3000 sat/kvB.
|
|
25
|
-
// A typical spendable segwit P2WPKH txout is 31 bytes big, and will
|
|
26
|
-
// need a CTxIn of at least 67 bytes to spend:
|
|
27
|
-
// so dust is a spendable txout less than
|
|
28
|
-
// 98*dustRelayFee/1000 (in satoshis).
|
|
29
|
-
// 294 satoshis at the default rate of 3000 sat/kvB.
|
|
30
|
-
if (txout.scriptPubKey.IsUnspendable())
|
|
31
|
-
return 0;
|
|
32
|
-
|
|
33
|
-
size_t nSize = GetSerializeSize(txout);
|
|
34
|
-
int witnessversion = 0;
|
|
35
|
-
std::vector<unsigned char> witnessprogram;
|
|
36
|
-
|
|
37
|
-
// Note this computation is for spending a Segwit v0 P2WPKH output (a 33 bytes
|
|
38
|
-
// public key + an ECDSA signature). For Segwit v1 Taproot outputs the minimum
|
|
39
|
-
// satisfaction is lower (a single BIP340 signature) but this computation was
|
|
40
|
-
// kept to not further reduce the dust level.
|
|
41
|
-
// See discussion in https://github.com/bitcoin/bitcoin/pull/22779 for details.
|
|
42
|
-
if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
|
|
43
|
-
// sum the sizes of the parts of a transaction input
|
|
44
|
-
// with 75% segwit discount applied to the script size.
|
|
45
|
-
nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
|
|
46
|
-
} else {
|
|
47
|
-
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return dustRelayFeeIn.GetFee(nSize);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
|
54
|
-
{
|
|
55
|
-
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
15
|
bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
|
|
59
16
|
{
|
|
60
17
|
std::vector<std::vector<unsigned char> > vSolutions;
|
|
@@ -78,7 +35,7 @@ bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
|
|
|
78
35
|
return true;
|
|
79
36
|
}
|
|
80
37
|
|
|
81
|
-
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig,
|
|
38
|
+
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason)
|
|
82
39
|
{
|
|
83
40
|
if (tx.nVersion > TX_MAX_STANDARD_VERSION || tx.nVersion < 1) {
|
|
84
41
|
reason = "version";
|
|
@@ -128,9 +85,6 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR
|
|
|
128
85
|
else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
|
|
129
86
|
reason = "bare-multisig";
|
|
130
87
|
return false;
|
|
131
|
-
} else if (IsDust(txout, dust_relay_fee)) {
|
|
132
|
-
reason = "dust";
|
|
133
|
-
return false;
|
|
134
88
|
}
|
|
135
89
|
}
|
|
136
90
|
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
#define BITCOIN_POLICY_POLICY_H
|
|
8
8
|
|
|
9
9
|
#include <consensus/consensus.h>
|
|
10
|
-
#include <policy/feerate.h>
|
|
11
10
|
#include <script/interpreter.h>
|
|
12
11
|
#include <script/standard.h>
|
|
13
12
|
|
|
@@ -18,8 +17,6 @@ class CTxOut;
|
|
|
18
17
|
|
|
19
18
|
/** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/
|
|
20
19
|
static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000;
|
|
21
|
-
/** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/
|
|
22
|
-
static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000;
|
|
23
20
|
/** The maximum weight for transactions we're willing to relay/mine */
|
|
24
21
|
static const unsigned int MAX_STANDARD_TX_WEIGHT = 400000;
|
|
25
22
|
/** The minimum non-witness size for transactions we're willing to relay/mine (1 segwit input + 1 P2WPKH output = 82 bytes) */
|
|
@@ -30,8 +27,6 @@ static const unsigned int MAX_P2SH_SIGOPS = 15;
|
|
|
30
27
|
static const unsigned int MAX_STANDARD_TX_SIGOPS_COST = MAX_BLOCK_SIGOPS_COST/5;
|
|
31
28
|
/** Default for -maxmempool, maximum megabytes of mempool memory usage */
|
|
32
29
|
static const unsigned int DEFAULT_MAX_MEMPOOL_SIZE = 300;
|
|
33
|
-
/** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or BIP 125 replacement **/
|
|
34
|
-
static const unsigned int DEFAULT_INCREMENTAL_RELAY_FEE = 1000;
|
|
35
30
|
/** Default for -bytespersigop */
|
|
36
31
|
static const unsigned int DEFAULT_BYTES_PER_SIGOP = 20;
|
|
37
32
|
/** Default for -permitbaremultisig */
|
|
@@ -46,12 +41,6 @@ static const unsigned int MAX_STANDARD_TAPSCRIPT_STACK_ITEM_SIZE = 80;
|
|
|
46
41
|
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
|
|
47
42
|
/** The maximum size of a standard ScriptSig */
|
|
48
43
|
static const unsigned int MAX_STANDARD_SCRIPTSIG_SIZE = 1650;
|
|
49
|
-
/** Min feerate for defining dust. Historically this has been based on the
|
|
50
|
-
* minRelayTxFee, however changing the dust limit changes which transactions are
|
|
51
|
-
* standard and should be done with care and ideally rarely. It makes sense to
|
|
52
|
-
* only increase the dust limit after prior releases were already not creating
|
|
53
|
-
* outputs below the new threshold */
|
|
54
|
-
static const unsigned int DUST_RELAY_TX_FEE = 3000;
|
|
55
44
|
/**
|
|
56
45
|
* Standard script verification flags that standard transactions will comply
|
|
57
46
|
* with. However scripts violating these flags may still be present in valid
|
|
@@ -73,7 +62,6 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE
|
|
|
73
62
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM |
|
|
74
63
|
SCRIPT_VERIFY_WITNESS_PUBKEYTYPE |
|
|
75
64
|
SCRIPT_VERIFY_CONST_SCRIPTCODE |
|
|
76
|
-
SCRIPT_VERIFY_TAPROOT |
|
|
77
65
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION |
|
|
78
66
|
SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS |
|
|
79
67
|
SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE;
|
|
@@ -85,23 +73,19 @@ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCR
|
|
|
85
73
|
static constexpr unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
|
|
86
74
|
LOCKTIME_MEDIAN_TIME_PAST;
|
|
87
75
|
|
|
88
|
-
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
|
|
89
|
-
|
|
90
|
-
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
|
|
91
|
-
|
|
92
76
|
bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType);
|
|
93
77
|
|
|
94
78
|
|
|
95
79
|
// Changing the default transaction version requires a two step process: first
|
|
96
80
|
// adapting relay policy by bumping TX_MAX_STANDARD_VERSION, and then later
|
|
97
81
|
// allowing the new transaction version in the wallet/RPC.
|
|
98
|
-
static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{
|
|
82
|
+
static constexpr decltype(CTransaction::nVersion) TX_MAX_STANDARD_VERSION{3};
|
|
99
83
|
|
|
100
84
|
/**
|
|
101
85
|
* Check for standard transaction types
|
|
102
86
|
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
|
|
103
87
|
*/
|
|
104
|
-
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig,
|
|
88
|
+
bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason);
|
|
105
89
|
/**
|
|
106
90
|
* Check for standard transaction types
|
|
107
91
|
* @param[in] mapInputs Map of previous transactions that have outputs we're spending
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2016-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#include <policy/rbf.h>
|
|
6
|
-
|
|
7
|
-
#include <policy/settings.h>
|
|
8
|
-
#include <tinyformat.h>
|
|
9
|
-
#include <util/moneystr.h>
|
|
10
|
-
#include <util/rbf.h>
|
|
11
|
-
|
|
12
|
-
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool)
|
|
13
|
-
{
|
|
14
|
-
AssertLockHeld(pool.cs);
|
|
15
|
-
|
|
16
|
-
CTxMemPool::setEntries ancestors;
|
|
17
|
-
|
|
18
|
-
// First check the transaction itself.
|
|
19
|
-
if (SignalsOptInRBF(tx)) {
|
|
20
|
-
return RBFTransactionState::REPLACEABLE_BIP125;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// If this transaction is not in our mempool, then we can't be sure
|
|
24
|
-
// we will know about all its inputs.
|
|
25
|
-
if (!pool.exists(GenTxid::Txid(tx.GetHash()))) {
|
|
26
|
-
return RBFTransactionState::UNKNOWN;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// If all the inputs have nSequence >= maxint-1, it still might be
|
|
30
|
-
// signaled for RBF if any unconfirmed parents have signaled.
|
|
31
|
-
uint64_t noLimit = std::numeric_limits<uint64_t>::max();
|
|
32
|
-
std::string dummy;
|
|
33
|
-
CTxMemPoolEntry entry = *pool.mapTx.find(tx.GetHash());
|
|
34
|
-
pool.CalculateMemPoolAncestors(entry, ancestors, noLimit, noLimit, noLimit, noLimit, dummy, false);
|
|
35
|
-
|
|
36
|
-
for (CTxMemPool::txiter it : ancestors) {
|
|
37
|
-
if (SignalsOptInRBF(it->GetTx())) {
|
|
38
|
-
return RBFTransactionState::REPLACEABLE_BIP125;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return RBFTransactionState::FINAL;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx)
|
|
45
|
-
{
|
|
46
|
-
// If we don't have a local mempool we can only check the transaction itself.
|
|
47
|
-
return SignalsOptInRBF(tx) ? RBFTransactionState::REPLACEABLE_BIP125 : RBFTransactionState::UNKNOWN;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx,
|
|
51
|
-
CTxMemPool& pool,
|
|
52
|
-
const CTxMemPool::setEntries& iters_conflicting,
|
|
53
|
-
CTxMemPool::setEntries& all_conflicts)
|
|
54
|
-
{
|
|
55
|
-
AssertLockHeld(pool.cs);
|
|
56
|
-
const uint256 txid = tx.GetHash();
|
|
57
|
-
uint64_t nConflictingCount = 0;
|
|
58
|
-
for (const auto& mi : iters_conflicting) {
|
|
59
|
-
nConflictingCount += mi->GetCountWithDescendants();
|
|
60
|
-
// BIP125 Rule #5: don't consider replacing more than MAX_BIP125_REPLACEMENT_CANDIDATES
|
|
61
|
-
// entries from the mempool. This potentially overestimates the number of actual
|
|
62
|
-
// descendants (i.e. if multiple conflicts share a descendant, it will be counted multiple
|
|
63
|
-
// times), but we just want to be conservative to avoid doing too much work.
|
|
64
|
-
if (nConflictingCount > MAX_BIP125_REPLACEMENT_CANDIDATES) {
|
|
65
|
-
return strprintf("rejecting replacement %s; too many potential replacements (%d > %d)\n",
|
|
66
|
-
txid.ToString(),
|
|
67
|
-
nConflictingCount,
|
|
68
|
-
MAX_BIP125_REPLACEMENT_CANDIDATES);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// Calculate the set of all transactions that would have to be evicted.
|
|
72
|
-
for (CTxMemPool::txiter it : iters_conflicting) {
|
|
73
|
-
pool.CalculateDescendants(it, all_conflicts);
|
|
74
|
-
}
|
|
75
|
-
return std::nullopt;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx,
|
|
79
|
-
const CTxMemPool& pool,
|
|
80
|
-
const CTxMemPool::setEntries& iters_conflicting)
|
|
81
|
-
{
|
|
82
|
-
AssertLockHeld(pool.cs);
|
|
83
|
-
std::set<uint256> parents_of_conflicts;
|
|
84
|
-
for (const auto& mi : iters_conflicting) {
|
|
85
|
-
for (const CTxIn& txin : mi->GetTx().vin) {
|
|
86
|
-
parents_of_conflicts.insert(txin.prevout.hash);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
for (unsigned int j = 0; j < tx.vin.size(); j++) {
|
|
91
|
-
// BIP125 Rule #2: We don't want to accept replacements that require low feerate junk to be
|
|
92
|
-
// mined first. Ideally we'd keep track of the ancestor feerates and make the decision
|
|
93
|
-
// based on that, but for now requiring all new inputs to be confirmed works.
|
|
94
|
-
//
|
|
95
|
-
// Note that if you relax this to make RBF a little more useful, this may break the
|
|
96
|
-
// CalculateMempoolAncestors RBF relaxation which subtracts the conflict count/size from the
|
|
97
|
-
// descendant limit.
|
|
98
|
-
if (!parents_of_conflicts.count(tx.vin[j].prevout.hash)) {
|
|
99
|
-
// Rather than check the UTXO set - potentially expensive - it's cheaper to just check
|
|
100
|
-
// if the new input refers to a tx that's in the mempool.
|
|
101
|
-
if (pool.exists(GenTxid::Txid(tx.vin[j].prevout.hash))) {
|
|
102
|
-
return strprintf("replacement %s adds unconfirmed input, idx %d",
|
|
103
|
-
tx.GetHash().ToString(), j);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return std::nullopt;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
|
|
111
|
-
const std::set<uint256>& direct_conflicts,
|
|
112
|
-
const uint256& txid)
|
|
113
|
-
{
|
|
114
|
-
for (CTxMemPool::txiter ancestorIt : ancestors) {
|
|
115
|
-
const uint256& hashAncestor = ancestorIt->GetTx().GetHash();
|
|
116
|
-
if (direct_conflicts.count(hashAncestor)) {
|
|
117
|
-
return strprintf("%s spends conflicting transaction %s",
|
|
118
|
-
txid.ToString(),
|
|
119
|
-
hashAncestor.ToString());
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return std::nullopt;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting,
|
|
126
|
-
CFeeRate replacement_feerate,
|
|
127
|
-
const uint256& txid)
|
|
128
|
-
{
|
|
129
|
-
for (const auto& mi : iters_conflicting) {
|
|
130
|
-
// Don't allow the replacement to reduce the feerate of the mempool.
|
|
131
|
-
//
|
|
132
|
-
// We usually don't want to accept replacements with lower feerates than what they replaced
|
|
133
|
-
// as that would lower the feerate of the next block. Requiring that the feerate always be
|
|
134
|
-
// increased is also an easy-to-reason about way to prevent DoS attacks via replacements.
|
|
135
|
-
//
|
|
136
|
-
// We only consider the feerates of transactions being directly replaced, not their indirect
|
|
137
|
-
// descendants. While that does mean high feerate children are ignored when deciding whether
|
|
138
|
-
// or not to replace, we do require the replacement to pay more overall fees too, mitigating
|
|
139
|
-
// most cases.
|
|
140
|
-
CFeeRate original_feerate(mi->GetModifiedFee(), mi->GetTxSize());
|
|
141
|
-
if (replacement_feerate <= original_feerate) {
|
|
142
|
-
return strprintf("rejecting replacement %s; new feerate %s <= old feerate %s",
|
|
143
|
-
txid.ToString(),
|
|
144
|
-
replacement_feerate.ToString(),
|
|
145
|
-
original_feerate.ToString());
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
return std::nullopt;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
std::optional<std::string> PaysForRBF(CAmount original_fees,
|
|
152
|
-
CAmount replacement_fees,
|
|
153
|
-
size_t replacement_vsize,
|
|
154
|
-
CFeeRate relay_fee,
|
|
155
|
-
const uint256& txid)
|
|
156
|
-
{
|
|
157
|
-
// BIP125 Rule #3: The replacement fees must be greater than or equal to fees of the
|
|
158
|
-
// transactions it replaces, otherwise the bandwidth used by those conflicting transactions
|
|
159
|
-
// would not be paid for.
|
|
160
|
-
if (replacement_fees < original_fees) {
|
|
161
|
-
return strprintf("rejecting replacement %s, less fees than conflicting txs; %s < %s",
|
|
162
|
-
txid.ToString(), FormatMoney(replacement_fees), FormatMoney(original_fees));
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// BIP125 Rule #4: The new transaction must pay for its own bandwidth. Otherwise, we have a DoS
|
|
166
|
-
// vector where attackers can cause a transaction to be replaced (and relayed) repeatedly by
|
|
167
|
-
// increasing the fee by tiny amounts.
|
|
168
|
-
CAmount additional_fees = replacement_fees - original_fees;
|
|
169
|
-
if (additional_fees < relay_fee.GetFee(replacement_vsize)) {
|
|
170
|
-
return strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
|
|
171
|
-
txid.ToString(),
|
|
172
|
-
FormatMoney(additional_fees),
|
|
173
|
-
FormatMoney(relay_fee.GetFee(replacement_vsize)));
|
|
174
|
-
}
|
|
175
|
-
return std::nullopt;
|
|
176
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2016-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#ifndef BITCOIN_POLICY_RBF_H
|
|
6
|
-
#define BITCOIN_POLICY_RBF_H
|
|
7
|
-
|
|
8
|
-
#include <primitives/transaction.h>
|
|
9
|
-
#include <txmempool.h>
|
|
10
|
-
#include <uint256.h>
|
|
11
|
-
|
|
12
|
-
#include <optional>
|
|
13
|
-
#include <string>
|
|
14
|
-
|
|
15
|
-
/** Maximum number of transactions that can be replaced by BIP125 RBF (Rule #5). This includes all
|
|
16
|
-
* mempool conflicts and their descendants. */
|
|
17
|
-
static constexpr uint32_t MAX_BIP125_REPLACEMENT_CANDIDATES{100};
|
|
18
|
-
|
|
19
|
-
/** The rbf state of unconfirmed transactions */
|
|
20
|
-
enum class RBFTransactionState {
|
|
21
|
-
/** Unconfirmed tx that does not signal rbf and is not in the mempool */
|
|
22
|
-
UNKNOWN,
|
|
23
|
-
/** Either this tx or a mempool ancestor signals rbf */
|
|
24
|
-
REPLACEABLE_BIP125,
|
|
25
|
-
/** Neither this tx nor a mempool ancestor signals rbf */
|
|
26
|
-
FINAL,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Determine whether an unconfirmed transaction is signaling opt-in to RBF
|
|
31
|
-
* according to BIP 125
|
|
32
|
-
* This involves checking sequence numbers of the transaction, as well
|
|
33
|
-
* as the sequence numbers of all in-mempool ancestors.
|
|
34
|
-
*
|
|
35
|
-
* @param tx The unconfirmed transaction
|
|
36
|
-
* @param pool The mempool, which may contain the tx
|
|
37
|
-
*
|
|
38
|
-
* @return The rbf state
|
|
39
|
-
*/
|
|
40
|
-
RBFTransactionState IsRBFOptIn(const CTransaction& tx, const CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
|
|
41
|
-
RBFTransactionState IsRBFOptInEmptyMempool(const CTransaction& tx);
|
|
42
|
-
|
|
43
|
-
/** Get all descendants of iters_conflicting. Also enforce BIP125 Rule #5, "The number of original
|
|
44
|
-
* transactions to be replaced and their descendant transactions which will be evicted from the
|
|
45
|
-
* mempool must not exceed a total of 100 transactions." Quit as early as possible. There cannot be
|
|
46
|
-
* more than MAX_BIP125_REPLACEMENT_CANDIDATES potential entries.
|
|
47
|
-
* @param[in] iters_conflicting The set of iterators to mempool entries.
|
|
48
|
-
* @param[out] all_conflicts Populated with all the mempool entries that would be replaced,
|
|
49
|
-
* which includes descendants of iters_conflicting. Not cleared at
|
|
50
|
-
* the start; any existing mempool entries will remain in the set.
|
|
51
|
-
* @returns an error message if Rule #5 is broken, otherwise a std::nullopt.
|
|
52
|
-
*/
|
|
53
|
-
std::optional<std::string> GetEntriesForConflicts(const CTransaction& tx, CTxMemPool& pool,
|
|
54
|
-
const CTxMemPool::setEntries& iters_conflicting,
|
|
55
|
-
CTxMemPool::setEntries& all_conflicts)
|
|
56
|
-
EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
|
|
57
|
-
|
|
58
|
-
/** BIP125 Rule #2: "The replacement transaction may only include an unconfirmed input if that input
|
|
59
|
-
* was included in one of the original transactions."
|
|
60
|
-
* @returns error message if Rule #2 is broken, otherwise std::nullopt. */
|
|
61
|
-
std::optional<std::string> HasNoNewUnconfirmed(const CTransaction& tx, const CTxMemPool& pool,
|
|
62
|
-
const CTxMemPool::setEntries& iters_conflicting)
|
|
63
|
-
EXCLUSIVE_LOCKS_REQUIRED(pool.cs);
|
|
64
|
-
|
|
65
|
-
/** Check the intersection between two sets of transactions (a set of mempool entries and a set of
|
|
66
|
-
* txids) to make sure they are disjoint.
|
|
67
|
-
* @param[in] ancestors Set of mempool entries corresponding to ancestors of the
|
|
68
|
-
* replacement transactions.
|
|
69
|
-
* @param[in] direct_conflicts Set of txids corresponding to the mempool conflicts
|
|
70
|
-
* (candidates to be replaced).
|
|
71
|
-
* @param[in] txid Transaction ID, included in the error message if violation occurs.
|
|
72
|
-
* @returns error message if the sets intersect, std::nullopt if they are disjoint.
|
|
73
|
-
*/
|
|
74
|
-
std::optional<std::string> EntriesAndTxidsDisjoint(const CTxMemPool::setEntries& ancestors,
|
|
75
|
-
const std::set<uint256>& direct_conflicts,
|
|
76
|
-
const uint256& txid);
|
|
77
|
-
|
|
78
|
-
/** Check that the feerate of the replacement transaction(s) is higher than the feerate of each
|
|
79
|
-
* of the transactions in iters_conflicting.
|
|
80
|
-
* @param[in] iters_conflicting The set of mempool entries.
|
|
81
|
-
* @returns error message if fees insufficient, otherwise std::nullopt.
|
|
82
|
-
*/
|
|
83
|
-
std::optional<std::string> PaysMoreThanConflicts(const CTxMemPool::setEntries& iters_conflicting,
|
|
84
|
-
CFeeRate replacement_feerate, const uint256& txid);
|
|
85
|
-
|
|
86
|
-
/** Enforce BIP125 Rule #3 "The replacement transaction pays an absolute fee of at least the sum
|
|
87
|
-
* paid by the original transactions." Enforce BIP125 Rule #4 "The replacement transaction must also
|
|
88
|
-
* pay for its own bandwidth at or above the rate set by the node's minimum relay fee setting."
|
|
89
|
-
* @param[in] original_fees Total modified fees of original transaction(s).
|
|
90
|
-
* @param[in] replacement_fees Total modified fees of replacement transaction(s).
|
|
91
|
-
* @param[in] replacement_vsize Total virtual size of replacement transaction(s).
|
|
92
|
-
* @param[in] relay_fee The node's minimum feerate for transaction relay.
|
|
93
|
-
* @param[in] txid Transaction ID, included in the error message if violation occurs.
|
|
94
|
-
* @returns error string if fees are insufficient, otherwise std::nullopt.
|
|
95
|
-
*/
|
|
96
|
-
std::optional<std::string> PaysForRBF(CAmount original_fees,
|
|
97
|
-
CAmount replacement_fees,
|
|
98
|
-
size_t replacement_vsize,
|
|
99
|
-
CFeeRate relay_fee,
|
|
100
|
-
const uint256& txid);
|
|
101
|
-
|
|
102
|
-
#endif // BITCOIN_POLICY_RBF_H
|
|
@@ -5,10 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
#include <policy/settings.h>
|
|
7
7
|
|
|
8
|
-
#include <policy/feerate.h>
|
|
9
8
|
#include <policy/policy.h>
|
|
10
9
|
|
|
11
10
|
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
|
12
|
-
CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
|
|
13
|
-
CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
|
|
14
11
|
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
|
|
@@ -8,18 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
#include <policy/policy.h>
|
|
10
10
|
|
|
11
|
-
class CFeeRate;
|
|
12
11
|
class CTransaction;
|
|
13
12
|
|
|
14
13
|
// Policy settings which are configurable at runtime.
|
|
15
|
-
extern CFeeRate incrementalRelayFee;
|
|
16
|
-
extern CFeeRate dustRelayFee;
|
|
17
14
|
extern unsigned int nBytesPerSigOp;
|
|
18
15
|
extern bool fIsBareMultisigStd;
|
|
19
16
|
|
|
20
17
|
static inline bool IsStandardTx(const CTransaction& tx, std::string& reason)
|
|
21
18
|
{
|
|
22
|
-
return IsStandardTx(tx, ::fIsBareMultisigStd,
|
|
19
|
+
return IsStandardTx(tx, ::fIsBareMultisigStd, reason);
|
|
23
20
|
}
|
|
24
21
|
|
|
25
22
|
static inline int64_t GetVirtualTransactionSize(int64_t weight, int64_t sigop_cost)
|
|
@@ -10,63 +10,53 @@
|
|
|
10
10
|
#include <primitives/block.h>
|
|
11
11
|
#include <uint256.h>
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
#include <bignum.h>
|
|
14
|
+
#include <chainparams.h>
|
|
15
|
+
#include <kernel.h>
|
|
16
|
+
|
|
17
|
+
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake, const Consensus::Params& params)
|
|
14
18
|
{
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
if (pindexLast == nullptr)
|
|
20
|
+
return UintToArith256(params.powLimit).GetCompact(); // genesis block
|
|
21
|
+
|
|
22
|
+
const CBlockIndex* pindexPrev = GetLastBlockIndex(pindexLast, fProofOfStake);
|
|
23
|
+
if (pindexPrev->pprev == nullptr)
|
|
24
|
+
return UintToArith256(params.bnInitialHashTarget).GetCompact(); // first block
|
|
25
|
+
const CBlockIndex* pindexPrevPrev = GetLastBlockIndex(pindexPrev->pprev, fProofOfStake);
|
|
26
|
+
if (pindexPrevPrev->pprev == nullptr)
|
|
27
|
+
return UintToArith256(params.bnInitialHashTarget).GetCompact(); // second block
|
|
28
|
+
|
|
29
|
+
int64_t nActualSpacing = pindexPrev->GetBlockTime() - pindexPrevPrev->GetBlockTime();
|
|
30
|
+
|
|
31
|
+
// rfc20
|
|
32
|
+
int64_t nHypotheticalSpacing = pindexLast->GetBlockTime() - pindexPrev->GetBlockTime();
|
|
33
|
+
if (!fProofOfStake && IsProtocolV12(pindexPrev) && (nHypotheticalSpacing > nActualSpacing))
|
|
34
|
+
nActualSpacing = nHypotheticalSpacing;
|
|
35
|
+
|
|
36
|
+
// peercoin: target change every block
|
|
37
|
+
// peercoin: retarget with exponential moving toward target spacing
|
|
38
|
+
CBigNum bnNew;
|
|
39
|
+
bnNew.SetCompact(pindexPrev->nBits);
|
|
40
|
+
if (Params().NetworkIDString() != CBaseChainParams::REGTEST) {
|
|
41
|
+
int64_t nTargetSpacing;
|
|
42
|
+
|
|
43
|
+
if (fProofOfStake) {
|
|
44
|
+
nTargetSpacing = params.nStakeTargetSpacing;
|
|
45
|
+
} else {
|
|
46
|
+
if (IsProtocolV09(pindexLast->nTime)) {
|
|
47
|
+
nTargetSpacing = params.nStakeTargetSpacing * 6;
|
|
48
|
+
} else {
|
|
49
|
+
nTargetSpacing = std::min(params.nTargetSpacingWorkMax, params.nStakeTargetSpacing * (1 + pindexLast->nHeight - pindexPrev->nHeight));
|
|
35
50
|
}
|
|
36
51
|
}
|
|
37
|
-
return pindexLast->nBits;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Go back by what we want to be 14 days worth of blocks
|
|
41
|
-
int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1);
|
|
42
|
-
assert(nHeightFirst >= 0);
|
|
43
|
-
const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst);
|
|
44
|
-
assert(pindexFirst);
|
|
45
52
|
|
|
46
|
-
|
|
47
|
-
|
|
53
|
+
int64_t nInterval = params.nTargetTimespan / nTargetSpacing;
|
|
54
|
+
bnNew *= ((nInterval - 1) * nTargetSpacing + nActualSpacing + nActualSpacing);
|
|
55
|
+
bnNew /= ((nInterval + 1) * nTargetSpacing);
|
|
56
|
+
}
|
|
48
57
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (params.fPowNoRetargeting)
|
|
52
|
-
return pindexLast->nBits;
|
|
53
|
-
|
|
54
|
-
// Limit adjustment step
|
|
55
|
-
int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
|
|
56
|
-
if (nActualTimespan < params.nPowTargetTimespan/4)
|
|
57
|
-
nActualTimespan = params.nPowTargetTimespan/4;
|
|
58
|
-
if (nActualTimespan > params.nPowTargetTimespan*4)
|
|
59
|
-
nActualTimespan = params.nPowTargetTimespan*4;
|
|
60
|
-
|
|
61
|
-
// Retarget
|
|
62
|
-
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
|
|
63
|
-
arith_uint256 bnNew;
|
|
64
|
-
bnNew.SetCompact(pindexLast->nBits);
|
|
65
|
-
bnNew *= nActualTimespan;
|
|
66
|
-
bnNew /= params.nPowTargetTimespan;
|
|
67
|
-
|
|
68
|
-
if (bnNew > bnPowLimit)
|
|
69
|
-
bnNew = bnPowLimit;
|
|
58
|
+
if (bnNew > CBigNum(params.powLimit))
|
|
59
|
+
bnNew = CBigNum(params.powLimit);
|
|
70
60
|
|
|
71
61
|
return bnNew.GetCompact();
|
|
72
62
|
}
|
|
@@ -14,8 +14,7 @@ class CBlockHeader;
|
|
|
14
14
|
class CBlockIndex;
|
|
15
15
|
class uint256;
|
|
16
16
|
|
|
17
|
-
unsigned int
|
|
18
|
-
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params&);
|
|
17
|
+
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake, const Consensus::Params& params);
|
|
19
18
|
|
|
20
19
|
/** Check whether a block hash satisfies the proof-of-work requirement specified by nBits */
|
|
21
20
|
bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
|
|
@@ -10,19 +10,21 @@
|
|
|
10
10
|
|
|
11
11
|
uint256 CBlockHeader::GetHash() const
|
|
12
12
|
{
|
|
13
|
-
|
|
13
|
+
CBlockHeader tmp(*this);
|
|
14
|
+
tmp.nFlags = 0;
|
|
15
|
+
return SerializeHash(tmp);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
std::string CBlock::ToString() const
|
|
17
19
|
{
|
|
18
20
|
std::stringstream s;
|
|
19
|
-
s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%u)\n",
|
|
21
|
+
s << strprintf("CBlock(hash=%s, ver=0x%08x, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, nFlags=%08x, vtx=%u)\n",
|
|
20
22
|
GetHash().ToString(),
|
|
21
23
|
nVersion,
|
|
22
24
|
hashPrevBlock.ToString(),
|
|
23
25
|
hashMerkleRoot.ToString(),
|
|
24
26
|
nTime, nBits, nNonce,
|
|
25
|
-
vtx.size());
|
|
27
|
+
nFlags, vtx.size());
|
|
26
28
|
for (const auto& tx : vtx) {
|
|
27
29
|
s << " " << tx->ToString() << "\n";
|
|
28
30
|
}
|
|
@@ -28,21 +28,34 @@ public:
|
|
|
28
28
|
uint32_t nBits;
|
|
29
29
|
uint32_t nNonce;
|
|
30
30
|
|
|
31
|
+
// peercoin: A copy from CBlockIndex.nFlags from other clients. We need this information because we are using headers-first syncronization.
|
|
32
|
+
uint32_t nFlags;
|
|
33
|
+
// peercoin: Used in CheckProofOfStake().
|
|
34
|
+
static const int32_t NORMAL_SERIALIZE_SIZE=80;
|
|
35
|
+
static const int32_t CURRENT_VERSION=4;
|
|
36
|
+
|
|
31
37
|
CBlockHeader()
|
|
32
38
|
{
|
|
33
39
|
SetNull();
|
|
34
40
|
}
|
|
35
41
|
|
|
36
|
-
SERIALIZE_METHODS(CBlockHeader, obj)
|
|
42
|
+
SERIALIZE_METHODS(CBlockHeader, obj)
|
|
43
|
+
{
|
|
44
|
+
READWRITE(obj.nVersion, obj.hashPrevBlock, obj.hashMerkleRoot, obj.nTime, obj.nBits, obj.nNonce);
|
|
45
|
+
// peercoin: do not serialize nFlags when computing hash
|
|
46
|
+
if (!(s.GetType() & SER_GETHASH) && s.GetType() & SER_POSMARKER)
|
|
47
|
+
READWRITE(obj.nFlags);
|
|
48
|
+
}
|
|
37
49
|
|
|
38
50
|
void SetNull()
|
|
39
51
|
{
|
|
40
|
-
nVersion =
|
|
52
|
+
nVersion = CURRENT_VERSION;
|
|
41
53
|
hashPrevBlock.SetNull();
|
|
42
54
|
hashMerkleRoot.SetNull();
|
|
43
55
|
nTime = 0;
|
|
44
56
|
nBits = 0;
|
|
45
57
|
nNonce = 0;
|
|
58
|
+
nFlags = 0;
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
bool IsNull() const
|
|
@@ -65,6 +78,9 @@ public:
|
|
|
65
78
|
// network and disk
|
|
66
79
|
std::vector<CTransactionRef> vtx;
|
|
67
80
|
|
|
81
|
+
// peercoin: block signature - signed by coin base txout[0]'s owner
|
|
82
|
+
std::vector<unsigned char> vchBlockSig;
|
|
83
|
+
|
|
68
84
|
// memory only
|
|
69
85
|
mutable bool fChecked;
|
|
70
86
|
|
|
@@ -83,6 +99,7 @@ public:
|
|
|
83
99
|
{
|
|
84
100
|
READWRITEAS(CBlockHeader, obj);
|
|
85
101
|
READWRITE(obj.vtx);
|
|
102
|
+
READWRITE(obj.vchBlockSig);
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
void SetNull()
|
|
@@ -90,6 +107,7 @@ public:
|
|
|
90
107
|
CBlockHeader::SetNull();
|
|
91
108
|
vtx.clear();
|
|
92
109
|
fChecked = false;
|
|
110
|
+
vchBlockSig.clear();
|
|
93
111
|
}
|
|
94
112
|
|
|
95
113
|
CBlockHeader GetBlockHeader() const
|
|
@@ -101,9 +119,37 @@ public:
|
|
|
101
119
|
block.nTime = nTime;
|
|
102
120
|
block.nBits = nBits;
|
|
103
121
|
block.nNonce = nNonce;
|
|
122
|
+
block.nFlags = nFlags;
|
|
104
123
|
return block;
|
|
105
124
|
}
|
|
106
125
|
|
|
126
|
+
// peercoin: two types of block: proof-of-work or proof-of-stake
|
|
127
|
+
bool IsProofOfStake() const
|
|
128
|
+
{
|
|
129
|
+
return (vtx.size() > 1 && vtx[1]->IsCoinStake());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
bool IsProofOfWork() const
|
|
133
|
+
{
|
|
134
|
+
return !IsProofOfStake();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
std::pair<COutPoint, unsigned int> GetProofOfStake() const
|
|
138
|
+
{
|
|
139
|
+
return IsProofOfStake() ? std::make_pair(vtx[1]->vin[0].prevout, vtx[1]->nTime) : std::make_pair(COutPoint(), (unsigned int)0);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// peercoin: get max transaction timestamp
|
|
143
|
+
int64_t GetMaxTransactionTime() const
|
|
144
|
+
{
|
|
145
|
+
int64_t maxTransactionTime = 0;
|
|
146
|
+
for (const auto& tx : vtx)
|
|
147
|
+
maxTransactionTime = std::max(maxTransactionTime, (int64_t)tx->nTime);
|
|
148
|
+
return maxTransactionTime;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
unsigned int GetStakeEntropyBit() const; // peercoin: entropy bit for stake modifier if chosen by modifier
|
|
152
|
+
|
|
107
153
|
std::string ToString() const;
|
|
108
154
|
};
|
|
109
155
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <util/strencodings.h>
|
|
12
12
|
|
|
13
13
|
#include <assert.h>
|
|
14
|
+
#include <timedata.h>
|
|
14
15
|
|
|
15
16
|
std::string COutPoint::ToString() const
|
|
16
17
|
{
|
|
@@ -54,11 +55,11 @@ CTxOut::CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn)
|
|
|
54
55
|
|
|
55
56
|
std::string CTxOut::ToString() const
|
|
56
57
|
{
|
|
57
|
-
return strprintf("CTxOut(nValue=%d.%
|
|
58
|
+
return strprintf("CTxOut(nValue=%d.%06d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30));
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {}
|
|
61
|
-
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime) {}
|
|
61
|
+
CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nTime(GetAdjustedTime()), nLockTime(0) {}
|
|
62
|
+
CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nTime(tx.nTime), nLockTime(tx.nLockTime) {}
|
|
62
63
|
|
|
63
64
|
uint256 CMutableTransaction::GetHash() const
|
|
64
65
|
{
|
|
@@ -78,8 +79,8 @@ uint256 CTransaction::ComputeWitnessHash() const
|
|
|
78
79
|
return SerializeHash(*this, SER_GETHASH, 0);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
|
82
|
-
CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
|
82
|
+
CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nTime(tx.nTime), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
|
83
|
+
CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nTime(tx.nTime), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {}
|
|
83
84
|
|
|
84
85
|
CAmount CTransaction::GetValueOut() const
|
|
85
86
|
{
|
|
@@ -101,8 +102,10 @@ unsigned int CTransaction::GetTotalSize() const
|
|
|
101
102
|
std::string CTransaction::ToString() const
|
|
102
103
|
{
|
|
103
104
|
std::string str;
|
|
104
|
-
str +=
|
|
105
|
+
str += IsCoinBase()? "Coinbase" : (IsCoinStake()? "Coinstake" : "CTransaction");
|
|
106
|
+
str += strprintf("(hash=%s, nTime=%d, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n",
|
|
105
107
|
GetHash().ToString().substr(0,10),
|
|
108
|
+
nTime,
|
|
106
109
|
nVersion,
|
|
107
110
|
vin.size(),
|
|
108
111
|
vout.size(),
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include <serialize.h>
|
|
13
13
|
#include <uint256.h>
|
|
14
14
|
|
|
15
|
+
#include <version.h>
|
|
15
16
|
#include <tuple>
|
|
16
17
|
|
|
17
18
|
/**
|
|
@@ -171,6 +172,17 @@ public:
|
|
|
171
172
|
return (nValue == -1);
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
void SetEmpty()
|
|
176
|
+
{
|
|
177
|
+
nValue = 0;
|
|
178
|
+
scriptPubKey.clear();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
bool IsEmpty() const
|
|
182
|
+
{
|
|
183
|
+
return (nValue == 0 && scriptPubKey.empty());
|
|
184
|
+
}
|
|
185
|
+
|
|
174
186
|
friend bool operator==(const CTxOut& a, const CTxOut& b)
|
|
175
187
|
{
|
|
176
188
|
return (a.nValue == b.nValue &&
|
|
@@ -209,6 +221,11 @@ inline void UnserializeTransaction(TxType& tx, Stream& s) {
|
|
|
209
221
|
const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
|
|
210
222
|
|
|
211
223
|
s >> tx.nVersion;
|
|
224
|
+
if (tx.nVersion < 3)
|
|
225
|
+
s >> tx.nTime;
|
|
226
|
+
else
|
|
227
|
+
tx.nTime = 0;
|
|
228
|
+
|
|
212
229
|
unsigned char flags = 0;
|
|
213
230
|
tx.vin.clear();
|
|
214
231
|
tx.vout.clear();
|
|
@@ -248,6 +265,8 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
|
|
|
248
265
|
const bool fAllowWitness = !(s.GetVersion() & SERIALIZE_TRANSACTION_NO_WITNESS);
|
|
249
266
|
|
|
250
267
|
s << tx.nVersion;
|
|
268
|
+
if (tx.nVersion<3)
|
|
269
|
+
s << tx.nTime;
|
|
251
270
|
unsigned char flags = 0;
|
|
252
271
|
// Consistency check
|
|
253
272
|
if (fAllowWitness) {
|
|
@@ -272,7 +291,6 @@ inline void SerializeTransaction(const TxType& tx, Stream& s) {
|
|
|
272
291
|
s << tx.nLockTime;
|
|
273
292
|
}
|
|
274
293
|
|
|
275
|
-
|
|
276
294
|
/** The basic transaction that is broadcasted on the network and contained in
|
|
277
295
|
* blocks. A transaction can contain multiple inputs and outputs.
|
|
278
296
|
*/
|
|
@@ -280,7 +298,7 @@ class CTransaction
|
|
|
280
298
|
{
|
|
281
299
|
public:
|
|
282
300
|
// Default transaction version.
|
|
283
|
-
static const int32_t CURRENT_VERSION=
|
|
301
|
+
static const int32_t CURRENT_VERSION=3;
|
|
284
302
|
|
|
285
303
|
// The local variables are made const to prevent unintended modification
|
|
286
304
|
// without updating the cached hash value. However, CTransaction is not
|
|
@@ -290,6 +308,7 @@ public:
|
|
|
290
308
|
const std::vector<CTxIn> vin;
|
|
291
309
|
const std::vector<CTxOut> vout;
|
|
292
310
|
const int32_t nVersion;
|
|
311
|
+
const uint32_t nTime;
|
|
293
312
|
const uint32_t nLockTime;
|
|
294
313
|
|
|
295
314
|
private:
|
|
@@ -334,7 +353,13 @@ public:
|
|
|
334
353
|
|
|
335
354
|
bool IsCoinBase() const
|
|
336
355
|
{
|
|
337
|
-
return (vin.size() == 1 && vin[0].prevout.IsNull());
|
|
356
|
+
return (vin.size() == 1 && vin[0].prevout.IsNull() && vout.size() >= 1);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
bool IsCoinStake() const
|
|
360
|
+
{
|
|
361
|
+
// peercoin: the coin stake transaction is marked with the first output empty
|
|
362
|
+
return (vin.size() > 0 && (!vin[0].prevout.IsNull()) && vout.size() >= 2 && vout[0].IsEmpty());
|
|
338
363
|
}
|
|
339
364
|
|
|
340
365
|
friend bool operator==(const CTransaction& a, const CTransaction& b)
|
|
@@ -366,6 +391,7 @@ struct CMutableTransaction
|
|
|
366
391
|
std::vector<CTxIn> vin;
|
|
367
392
|
std::vector<CTxOut> vout;
|
|
368
393
|
int32_t nVersion;
|
|
394
|
+
uint32_t nTime;
|
|
369
395
|
uint32_t nLockTime;
|
|
370
396
|
|
|
371
397
|
CMutableTransaction();
|
|
@@ -181,6 +181,7 @@ const std::vector<std::string> &getAllNetMessageTypes()
|
|
|
181
181
|
return allNetMessageTypesVec;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
const unsigned int POW_HEADER_COOLING = 70;
|
|
184
185
|
/**
|
|
185
186
|
* Convert a service flag (NODE_*) to a human readable string.
|
|
186
187
|
* It supports unknown service flags which will be returned as "UNKNOWN[...]".
|
|
@@ -273,7 +273,7 @@ enum ServiceFlags : uint64_t {
|
|
|
273
273
|
// Nothing
|
|
274
274
|
NODE_NONE = 0,
|
|
275
275
|
// NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently
|
|
276
|
-
// set by all Bitcoin Core
|
|
276
|
+
// set by all Bitcoin Core nodes, and is unset by SPV clients or other light clients.
|
|
277
277
|
NODE_NETWORK = (1 << 0),
|
|
278
278
|
// NODE_BLOOM means the node is capable and willing to handle bloom-filtered connections.
|
|
279
279
|
// Bitcoin Core nodes used to support this by default, without advertising this bit,
|
|
@@ -511,4 +511,7 @@ public:
|
|
|
511
511
|
/** Convert a TX/WITNESS_TX/WTX CInv to a GenTxid. */
|
|
512
512
|
GenTxid ToGenTxid(const CInv& inv);
|
|
513
513
|
|
|
514
|
+
/** peercoin: How much temperature a PoW header will remove */
|
|
515
|
+
extern const unsigned int POW_HEADER_COOLING;
|
|
516
|
+
|
|
514
517
|
#endif // BITCOIN_PROTOCOL_H
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
#include <attributes.h>
|
|
9
9
|
#include <node/transaction.h>
|
|
10
|
-
#include <policy/feerate.h>
|
|
11
10
|
#include <primitives/transaction.h>
|
|
12
11
|
#include <pubkey.h>
|
|
13
12
|
#include <script/keyorigin.h>
|
|
@@ -33,7 +33,6 @@
|
|
|
33
33
|
#include <univalue.h>
|
|
34
34
|
|
|
35
35
|
using node::GetTransaction;
|
|
36
|
-
using node::IsBlockPruned;
|
|
37
36
|
using node::NodeContext;
|
|
38
37
|
using node::ReadBlockFromDisk;
|
|
39
38
|
|
|
@@ -296,8 +295,6 @@ static bool rest_block(const std::any& context,
|
|
|
296
295
|
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
|
297
296
|
}
|
|
298
297
|
|
|
299
|
-
if (IsBlockPruned(pblockindex))
|
|
300
|
-
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not available (pruned data)");
|
|
301
298
|
|
|
302
299
|
if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus()))
|
|
303
300
|
return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found");
|
|
@@ -1348,6 +1348,10 @@ public:
|
|
|
1348
1348
|
void Serialize(S &s) const {
|
|
1349
1349
|
// Serialize nVersion
|
|
1350
1350
|
::Serialize(s, txTo.nVersion);
|
|
1351
|
+
if (txTo.nVersion < 3) {
|
|
1352
|
+
// Serialize nTime
|
|
1353
|
+
::Serialize(s, txTo.nTime);
|
|
1354
|
+
}
|
|
1351
1355
|
// Serialize vin
|
|
1352
1356
|
unsigned int nInputs = fAnyoneCanPay ? 1 : txTo.vin.size();
|
|
1353
1357
|
::WriteCompactSize(s, nInputs);
|
|
@@ -1620,6 +1624,10 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
|
|
|
1620
1624
|
CHashWriter ss(SER_GETHASH, 0);
|
|
1621
1625
|
// Version
|
|
1622
1626
|
ss << txTo.nVersion;
|
|
1627
|
+
if (txTo.nVersion < 3) {
|
|
1628
|
+
// nTime
|
|
1629
|
+
ss << txTo.nTime;
|
|
1630
|
+
}
|
|
1623
1631
|
// Input prevouts/nSequence (none/all, depending on flags)
|
|
1624
1632
|
ss << hashPrevouts;
|
|
1625
1633
|
ss << hashSequence;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
#include <util/strencodings.h>
|
|
9
9
|
|
|
10
|
+
#include <key.h>
|
|
10
11
|
#include <string>
|
|
11
12
|
|
|
12
13
|
std::string GetOpName(opcodetype opcode)
|
|
@@ -280,6 +281,19 @@ bool CScript::HasValidOps() const
|
|
|
280
281
|
return true;
|
|
281
282
|
}
|
|
282
283
|
|
|
284
|
+
void CScript::SetMultisig(int nRequired, const std::vector<CPubKey>& keys)
|
|
285
|
+
{
|
|
286
|
+
this->clear();
|
|
287
|
+
|
|
288
|
+
*this << EncodeOP_N(nRequired);
|
|
289
|
+
for (const auto& pubkey : keys)
|
|
290
|
+
{
|
|
291
|
+
std::vector<unsigned char> vchPubKey(pubkey.begin(), pubkey.end());
|
|
292
|
+
*this << vchPubKey;
|
|
293
|
+
}
|
|
294
|
+
*this << EncodeOP_N(keys.size()) << OP_CHECKMULTISIG;
|
|
295
|
+
}
|
|
296
|
+
|
|
283
297
|
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet)
|
|
284
298
|
{
|
|
285
299
|
opcodeRet = OP_INVALIDOPCODE;
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
#include <string>
|
|
21
21
|
#include <vector>
|
|
22
22
|
|
|
23
|
+
class CPubKey;
|
|
24
|
+
|
|
23
25
|
// Maximum number of bytes pushable to the stack
|
|
24
26
|
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520;
|
|
25
27
|
|
|
@@ -223,7 +225,7 @@ class CScriptNum
|
|
|
223
225
|
* The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1],
|
|
224
226
|
* but results may overflow (and are valid as long as they are not used in a subsequent
|
|
225
227
|
* numeric operation). CScriptNum enforces those semantics by storing results as
|
|
226
|
-
* an
|
|
228
|
+
* an int64_t and allowing out-of-range values to be returned as a vector of bytes but
|
|
227
229
|
* throwing an exception if arithmetic is done or the result is interpreted as an integer.
|
|
228
230
|
*/
|
|
229
231
|
public:
|
|
@@ -552,6 +554,8 @@ public:
|
|
|
552
554
|
CScriptBase::clear();
|
|
553
555
|
shrink_to_fit();
|
|
554
556
|
}
|
|
557
|
+
|
|
558
|
+
void SetMultisig(int nRequired, const std::vector<CPubKey>& keys);
|
|
555
559
|
};
|
|
556
560
|
|
|
557
561
|
struct CScriptWitness
|
|
@@ -622,6 +622,10 @@ bool SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore,
|
|
|
622
622
|
{
|
|
623
623
|
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
|
|
624
624
|
|
|
625
|
+
// we don't need nTime anymore
|
|
626
|
+
if (mtx.nVersion >= 3)
|
|
627
|
+
mtx.nTime = 0;
|
|
628
|
+
|
|
625
629
|
// Use CTransaction for the constant parts of the
|
|
626
630
|
// transaction to avoid rehashing.
|
|
627
631
|
const CTransaction txConst(mtx);
|
|
@@ -32,10 +32,10 @@ public:
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
* Default setting for nMaxDatacarrierBytes.
|
|
35
|
+
* Default setting for nMaxDatacarrierBytes. 256 bytes of data, +1 for OP_RETURN,
|
|
36
36
|
* +2 for the pushdata opcodes.
|
|
37
37
|
*/
|
|
38
|
-
static const unsigned int MAX_OP_RETURN_RELAY =
|
|
38
|
+
static const unsigned int MAX_OP_RETURN_RELAY = 259;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* A data carrying output is an unspendable output containing data. The script
|
|
@@ -131,6 +131,8 @@ enum
|
|
|
131
131
|
SER_NETWORK = (1 << 0),
|
|
132
132
|
SER_DISK = (1 << 1),
|
|
133
133
|
SER_GETHASH = (1 << 2),
|
|
134
|
+
|
|
135
|
+
SER_POSMARKER = (1 << 18), // peercoin: for sending block headers with PoS marker, to allow headers-first syncronization
|
|
134
136
|
};
|
|
135
137
|
|
|
136
138
|
//! Convert the reference base type to X, without changing constness or reference type.
|
|
@@ -984,9 +986,10 @@ class CSizeComputer
|
|
|
984
986
|
protected:
|
|
985
987
|
size_t nSize;
|
|
986
988
|
|
|
989
|
+
const int nType;
|
|
987
990
|
const int nVersion;
|
|
988
991
|
public:
|
|
989
|
-
explicit CSizeComputer(int nVersionIn) : nSize(0), nVersion(nVersionIn) {}
|
|
992
|
+
explicit CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {}
|
|
990
993
|
|
|
991
994
|
void write(Span<const std::byte> src)
|
|
992
995
|
{
|
|
@@ -1010,6 +1013,7 @@ public:
|
|
|
1010
1013
|
return nSize;
|
|
1011
1014
|
}
|
|
1012
1015
|
|
|
1016
|
+
int GetType() const { return nType; }
|
|
1013
1017
|
int GetVersion() const { return nVersion; }
|
|
1014
1018
|
};
|
|
1015
1019
|
|
|
@@ -1083,15 +1087,15 @@ inline void WriteCompactSize(CSizeComputer &s, uint64_t nSize)
|
|
|
1083
1087
|
}
|
|
1084
1088
|
|
|
1085
1089
|
template <typename T>
|
|
1086
|
-
size_t GetSerializeSize(const T& t, int nVersion = 0)
|
|
1090
|
+
size_t GetSerializeSize(const T& t, int nType, int nVersion = 0)
|
|
1087
1091
|
{
|
|
1088
|
-
return (CSizeComputer(nVersion) << t).size();
|
|
1092
|
+
return (CSizeComputer(nType, nVersion) << t).size();
|
|
1089
1093
|
}
|
|
1090
1094
|
|
|
1091
1095
|
template <typename... T>
|
|
1092
1096
|
size_t GetSerializeSizeMany(int nVersion, const T&... t)
|
|
1093
1097
|
{
|
|
1094
|
-
CSizeComputer sc(nVersion);
|
|
1098
|
+
CSizeComputer sc(0, nVersion);
|
|
1095
1099
|
SerializeMany(sc, t...);
|
|
1096
1100
|
return sc.size();
|
|
1097
1101
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#include <stdint.h>
|
|
2
|
+
|
|
3
|
+
// needed when linking transaction.cpp, since we are not going to pull real GetAdjustedTime from timedata.cpp
|
|
4
|
+
int64_t GetAdjustedTime()
|
|
5
|
+
{
|
|
6
|
+
return 0;
|
|
7
|
+
}
|
|
@@ -322,7 +322,16 @@ bool CBlockTreeDB::LoadBlockIndexGuts(const Consensus::Params& consensusParams,
|
|
|
322
322
|
pindexNew->nStatus = diskindex.nStatus;
|
|
323
323
|
pindexNew->nTx = diskindex.nTx;
|
|
324
324
|
|
|
325
|
-
|
|
325
|
+
// peercoin related block index fields
|
|
326
|
+
pindexNew->nMint = diskindex.nMint;
|
|
327
|
+
pindexNew->nMoneySupply = diskindex.nMoneySupply;
|
|
328
|
+
pindexNew->nFlags = diskindex.nFlags;
|
|
329
|
+
pindexNew->nStakeModifier = diskindex.nStakeModifier;
|
|
330
|
+
pindexNew->prevoutStake = diskindex.prevoutStake;
|
|
331
|
+
pindexNew->nStakeTime = diskindex.nStakeTime;
|
|
332
|
+
pindexNew->hashProofOfStake = diskindex.hashProofOfStake;
|
|
333
|
+
|
|
334
|
+
if (pindexNew->IsProofOfWork() && !CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, consensusParams)) {
|
|
326
335
|
return error("%s: CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
|
|
327
336
|
}
|
|
328
337
|
|
|
@@ -353,6 +362,12 @@ public:
|
|
|
353
362
|
//! at which height this transaction was included in the active block chain
|
|
354
363
|
int nHeight;
|
|
355
364
|
|
|
365
|
+
// peercoin: whether transaction is a coinstake
|
|
366
|
+
bool fCoinStake;
|
|
367
|
+
|
|
368
|
+
// peercoin: transaction timestamp
|
|
369
|
+
unsigned int nTime;
|
|
370
|
+
|
|
356
371
|
//! empty constructor
|
|
357
372
|
CCoins() : fCoinBase(false), vout(0), nHeight(0) { }
|
|
358
373
|
|
|
@@ -435,7 +450,7 @@ bool CCoinsViewDB::Upgrade() {
|
|
|
435
450
|
COutPoint outpoint(key.second, 0);
|
|
436
451
|
for (size_t i = 0; i < old_coins.vout.size(); ++i) {
|
|
437
452
|
if (!old_coins.vout[i].IsNull() && !old_coins.vout[i].scriptPubKey.IsUnspendable()) {
|
|
438
|
-
Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase);
|
|
453
|
+
Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase, old_coins.fCoinStake, old_coins.nTime);
|
|
439
454
|
outpoint.n = i;
|
|
440
455
|
CoinEntry entry(&outpoint);
|
|
441
456
|
batch.Write(entry, newcoin);
|
|
@@ -10,15 +10,17 @@
|
|
|
10
10
|
#include <consensus/consensus.h>
|
|
11
11
|
#include <consensus/tx_verify.h>
|
|
12
12
|
#include <consensus/validation.h>
|
|
13
|
-
#include <
|
|
13
|
+
#include <kernel.h>
|
|
14
14
|
#include <policy/policy.h>
|
|
15
15
|
#include <policy/settings.h>
|
|
16
16
|
#include <reverse_iterator.h>
|
|
17
|
+
#include <timedata.h>
|
|
17
18
|
#include <util/moneystr.h>
|
|
18
19
|
#include <util/system.h>
|
|
19
20
|
#include <util/time.h>
|
|
20
21
|
#include <validationinterface.h>
|
|
21
22
|
|
|
23
|
+
#include <chainparams.h>
|
|
22
24
|
#include <cmath>
|
|
23
25
|
#include <optional>
|
|
24
26
|
|
|
@@ -461,8 +463,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee,
|
|
|
461
463
|
assert(int(nSigOpCostWithAncestors) >= 0);
|
|
462
464
|
}
|
|
463
465
|
|
|
464
|
-
CTxMemPool::CTxMemPool(
|
|
465
|
-
: m_check_ratio(check_ratio)
|
|
466
|
+
CTxMemPool::CTxMemPool(int check_ratio)
|
|
467
|
+
: m_check_ratio(check_ratio)
|
|
466
468
|
{
|
|
467
469
|
_clear(); //lock free clear
|
|
468
470
|
}
|
|
@@ -483,7 +485,7 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n)
|
|
|
483
485
|
nTransactionsUpdated += n;
|
|
484
486
|
}
|
|
485
487
|
|
|
486
|
-
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAncestors
|
|
488
|
+
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAncestors)
|
|
487
489
|
{
|
|
488
490
|
// Add to memory pool without checking anything.
|
|
489
491
|
// Used by AcceptToMemoryPool(), which DOES do
|
|
@@ -526,10 +528,6 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
|
|
|
526
528
|
|
|
527
529
|
nTransactionsUpdated++;
|
|
528
530
|
totalTxSize += entry.GetTxSize();
|
|
529
|
-
m_total_fee += entry.GetFee();
|
|
530
|
-
if (minerPolicyEstimator) {
|
|
531
|
-
minerPolicyEstimator->processTransaction(entry, validFeeEstimate);
|
|
532
|
-
}
|
|
533
531
|
|
|
534
532
|
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
|
|
535
533
|
newit->vTxHashesIdx = vTxHashes.size() - 1;
|
|
@@ -549,12 +547,9 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
|
|
549
547
|
GetMainSignals().TransactionRemovedFromMempool(it->GetSharedTx(), reason, mempool_sequence);
|
|
550
548
|
}
|
|
551
549
|
|
|
552
|
-
const uint256 hash = it->GetTx().GetHash();
|
|
553
550
|
for (const CTxIn& txin : it->GetTx().vin)
|
|
554
551
|
mapNextTx.erase(txin.prevout);
|
|
555
552
|
|
|
556
|
-
RemoveUnbroadcastTx(hash, true /* add logging because unchecked */ );
|
|
557
|
-
|
|
558
553
|
if (vTxHashes.size() > 1) {
|
|
559
554
|
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back());
|
|
560
555
|
vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx;
|
|
@@ -570,7 +565,6 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
|
|
570
565
|
cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst());
|
|
571
566
|
mapTx.erase(it);
|
|
572
567
|
nTransactionsUpdated++;
|
|
573
|
-
if (minerPolicyEstimator) {minerPolicyEstimator->removeTx(hash, false);}
|
|
574
568
|
}
|
|
575
569
|
|
|
576
570
|
// Calculates descendants of entry that are not already in setDescendants, and adds to
|
|
@@ -685,8 +679,6 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
|
|
|
685
679
|
if (i != mapTx.end())
|
|
686
680
|
entries.push_back(&*i);
|
|
687
681
|
}
|
|
688
|
-
// Before the txs in the new block have been removed from the mempool, update policy estimates
|
|
689
|
-
if (minerPolicyEstimator) {minerPolicyEstimator->processBlock(nBlockHeight, entries);}
|
|
690
682
|
for (const auto& tx : vtx)
|
|
691
683
|
{
|
|
692
684
|
txiter it = mapTx.find(tx->GetHash());
|
|
@@ -698,8 +690,6 @@ void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigne
|
|
|
698
690
|
removeConflicts(*tx);
|
|
699
691
|
ClearPrioritisation(tx->GetHash());
|
|
700
692
|
}
|
|
701
|
-
lastRollingFeeUpdate = GetTime();
|
|
702
|
-
blockSinceLastRollingFeeBump = true;
|
|
703
693
|
}
|
|
704
694
|
|
|
705
695
|
void CTxMemPool::_clear()
|
|
@@ -709,9 +699,6 @@ void CTxMemPool::_clear()
|
|
|
709
699
|
totalTxSize = 0;
|
|
710
700
|
m_total_fee = 0;
|
|
711
701
|
cachedInnerUsage = 0;
|
|
712
|
-
lastRollingFeeUpdate = GetTime();
|
|
713
|
-
blockSinceLastRollingFeeBump = false;
|
|
714
|
-
rollingMinimumFeeRate = 0;
|
|
715
702
|
++nTransactionsUpdated;
|
|
716
703
|
}
|
|
717
704
|
|
|
@@ -812,9 +799,9 @@ void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendhei
|
|
|
812
799
|
TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass
|
|
813
800
|
CAmount txfee = 0;
|
|
814
801
|
assert(!tx.IsCoinBase());
|
|
815
|
-
assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee));
|
|
802
|
+
assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee, Params().GetConsensus(), tx.nTime ? tx.nTime : GetAdjustedTime()));
|
|
816
803
|
for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout);
|
|
817
|
-
AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max());
|
|
804
|
+
AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max(), false, true);
|
|
818
805
|
}
|
|
819
806
|
for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) {
|
|
820
807
|
uint256 hash = it->second->GetHash();
|
|
@@ -1016,7 +1003,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
|
|
1016
1003
|
CTransactionRef ptx = mempool.get(outpoint.hash);
|
|
1017
1004
|
if (ptx) {
|
|
1018
1005
|
if (outpoint.n < ptx->vout.size()) {
|
|
1019
|
-
coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false);
|
|
1006
|
+
coin = Coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false, ptx->IsCoinStake(), ptx->nTime);
|
|
1020
1007
|
return true;
|
|
1021
1008
|
} else {
|
|
1022
1009
|
return false;
|
|
@@ -1028,7 +1015,7 @@ bool CCoinsViewMemPool::GetCoin(const COutPoint &outpoint, Coin &coin) const {
|
|
|
1028
1015
|
void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx)
|
|
1029
1016
|
{
|
|
1030
1017
|
for (unsigned int n = 0; n < tx->vout.size(); ++n) {
|
|
1031
|
-
m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false));
|
|
1018
|
+
m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false, tx->IsCoinStake(), tx->nTime));
|
|
1032
1019
|
}
|
|
1033
1020
|
}
|
|
1034
1021
|
|
|
@@ -1072,13 +1059,13 @@ int CTxMemPool::Expire(std::chrono::seconds time)
|
|
|
1072
1059
|
return stage.size();
|
|
1073
1060
|
}
|
|
1074
1061
|
|
|
1075
|
-
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry
|
|
1062
|
+
void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry)
|
|
1076
1063
|
{
|
|
1077
1064
|
setEntries setAncestors;
|
|
1078
1065
|
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
|
|
1079
1066
|
std::string dummy;
|
|
1080
1067
|
CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy);
|
|
1081
|
-
return addUnchecked(entry, setAncestors
|
|
1068
|
+
return addUnchecked(entry, setAncestors);
|
|
1082
1069
|
}
|
|
1083
1070
|
|
|
1084
1071
|
void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add)
|
|
@@ -1103,55 +1090,13 @@ void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add)
|
|
|
1103
1090
|
}
|
|
1104
1091
|
}
|
|
1105
1092
|
|
|
1106
|
-
CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const {
|
|
1107
|
-
LOCK(cs);
|
|
1108
|
-
if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0)
|
|
1109
|
-
return CFeeRate(llround(rollingMinimumFeeRate));
|
|
1110
|
-
|
|
1111
|
-
int64_t time = GetTime();
|
|
1112
|
-
if (time > lastRollingFeeUpdate + 10) {
|
|
1113
|
-
double halflife = ROLLING_FEE_HALFLIFE;
|
|
1114
|
-
if (DynamicMemoryUsage() < sizelimit / 4)
|
|
1115
|
-
halflife /= 4;
|
|
1116
|
-
else if (DynamicMemoryUsage() < sizelimit / 2)
|
|
1117
|
-
halflife /= 2;
|
|
1118
|
-
|
|
1119
|
-
rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife);
|
|
1120
|
-
lastRollingFeeUpdate = time;
|
|
1121
|
-
|
|
1122
|
-
if (rollingMinimumFeeRate < (double)incrementalRelayFee.GetFeePerK() / 2) {
|
|
1123
|
-
rollingMinimumFeeRate = 0;
|
|
1124
|
-
return CFeeRate(0);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
return std::max(CFeeRate(llround(rollingMinimumFeeRate)), incrementalRelayFee);
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) {
|
|
1131
|
-
AssertLockHeld(cs);
|
|
1132
|
-
if (rate.GetFeePerK() > rollingMinimumFeeRate) {
|
|
1133
|
-
rollingMinimumFeeRate = rate.GetFeePerK();
|
|
1134
|
-
blockSinceLastRollingFeeBump = false;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
1093
|
void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining) {
|
|
1139
1094
|
AssertLockHeld(cs);
|
|
1140
1095
|
|
|
1141
1096
|
unsigned nTxnRemoved = 0;
|
|
1142
|
-
CFeeRate maxFeeRateRemoved(0);
|
|
1143
1097
|
while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) {
|
|
1144
1098
|
indexed_transaction_set::index<descendant_score>::type::iterator it = mapTx.get<descendant_score>().begin();
|
|
1145
1099
|
|
|
1146
|
-
// We set the new mempool min fee to the feerate of the removed set, plus the
|
|
1147
|
-
// "minimum reasonable fee rate" (ie some value under which we consider txn
|
|
1148
|
-
// to have 0 fee). This way, we don't allow txn to enter mempool with feerate
|
|
1149
|
-
// equal to txn which were removed with no block in between.
|
|
1150
|
-
CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants());
|
|
1151
|
-
removed += incrementalRelayFee;
|
|
1152
|
-
trackPackageRemoved(removed);
|
|
1153
|
-
maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed);
|
|
1154
|
-
|
|
1155
1100
|
setEntries stage;
|
|
1156
1101
|
CalculateDescendants(mapTx.project<0>(it), stage);
|
|
1157
1102
|
nTxnRemoved += stage.size();
|
|
@@ -1172,10 +1117,6 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpends
|
|
|
1172
1117
|
}
|
|
1173
1118
|
}
|
|
1174
1119
|
}
|
|
1175
|
-
|
|
1176
|
-
if (maxFeeRateRemoved > CFeeRate(0)) {
|
|
1177
|
-
LogPrint(BCLog::MEMPOOL, "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString());
|
|
1178
|
-
}
|
|
1179
1120
|
}
|
|
1180
1121
|
|
|
1181
1122
|
uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const {
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
#include <coins.h>
|
|
18
18
|
#include <consensus/amount.h>
|
|
19
19
|
#include <indirectmap.h>
|
|
20
|
-
#include <policy/feerate.h>
|
|
21
20
|
#include <policy/packages.h>
|
|
22
21
|
#include <primitives/transaction.h>
|
|
23
22
|
#include <random.h>
|
|
@@ -318,8 +317,6 @@ struct entry_time {};
|
|
|
318
317
|
struct ancestor_score {};
|
|
319
318
|
struct index_by_wtxid {};
|
|
320
319
|
|
|
321
|
-
class CBlockPolicyEstimator;
|
|
322
|
-
|
|
323
320
|
/**
|
|
324
321
|
* Information about a mempool transaction.
|
|
325
322
|
*/
|
|
@@ -361,9 +358,6 @@ enum class MemPoolRemovalReason {
|
|
|
361
358
|
* local node), but not all transactions seen are added to the pool. For
|
|
362
359
|
* example, the following new transactions will not be added to the mempool:
|
|
363
360
|
* - a transaction which doesn't meet the minimum fee requirements.
|
|
364
|
-
* - a new transaction that double-spends an input of a transaction already in
|
|
365
|
-
* the pool where the new transaction does not meet the Replace-By-Fee
|
|
366
|
-
* requirements as defined in BIP 125.
|
|
367
361
|
* - a non-standard transaction.
|
|
368
362
|
*
|
|
369
363
|
* CTxMemPool::mapTx, and CTxMemPoolEntry bookkeeping:
|
|
@@ -431,24 +425,17 @@ class CTxMemPool
|
|
|
431
425
|
protected:
|
|
432
426
|
const int m_check_ratio; //!< Value n means that 1 times in n we check.
|
|
433
427
|
std::atomic<unsigned int> nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation
|
|
434
|
-
CBlockPolicyEstimator* const minerPolicyEstimator;
|
|
435
428
|
|
|
436
429
|
uint64_t totalTxSize GUARDED_BY(cs); //!< sum of all mempool tx's virtual sizes. Differs from serialized tx size since witness data is discounted. Defined in BIP 141.
|
|
437
430
|
CAmount m_total_fee GUARDED_BY(cs); //!< sum of all mempool tx's fees (NOT modified fee)
|
|
438
431
|
uint64_t cachedInnerUsage GUARDED_BY(cs); //!< sum of dynamic memory usage of all the map elements (NOT the maps themselves)
|
|
439
432
|
|
|
440
|
-
mutable int64_t lastRollingFeeUpdate GUARDED_BY(cs);
|
|
441
|
-
mutable bool blockSinceLastRollingFeeBump GUARDED_BY(cs);
|
|
442
|
-
mutable double rollingMinimumFeeRate GUARDED_BY(cs); //!< minimum fee to get into the pool, decreases exponentially
|
|
443
433
|
mutable Epoch m_epoch GUARDED_BY(cs);
|
|
444
|
-
|
|
445
434
|
// In-memory counter for external mempool tracking purposes.
|
|
446
435
|
// This number is incremented once every time a transaction
|
|
447
436
|
// is added or removed from the mempool for any reason.
|
|
448
437
|
mutable uint64_t m_sequence_number GUARDED_BY(cs){1};
|
|
449
438
|
|
|
450
|
-
void trackPackageRemoved(const CFeeRate& rate) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
451
|
-
|
|
452
439
|
bool m_is_loaded GUARDED_BY(cs){false};
|
|
453
440
|
|
|
454
441
|
public:
|
|
@@ -568,7 +555,7 @@ public:
|
|
|
568
555
|
* @param[in] estimator is used to estimate appropriate transaction fees.
|
|
569
556
|
* @param[in] check_ratio is the ratio used to determine how often sanity checks will run.
|
|
570
557
|
*/
|
|
571
|
-
explicit CTxMemPool(
|
|
558
|
+
explicit CTxMemPool(int check_ratio = 0);
|
|
572
559
|
|
|
573
560
|
/**
|
|
574
561
|
* If sanity-checking is turned on, check makes sure the pool is
|
|
@@ -585,8 +572,8 @@ public:
|
|
|
585
572
|
// Note that addUnchecked is ONLY called from ATMP outside of tests
|
|
586
573
|
// and any other callers may break wallet's in-mempool tracking (due to
|
|
587
574
|
// lack of CValidationInterface::TransactionAddedToMempool callbacks).
|
|
588
|
-
void addUnchecked(const CTxMemPoolEntry& entry
|
|
589
|
-
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors
|
|
575
|
+
void addUnchecked(const CTxMemPoolEntry& entry) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
|
|
576
|
+
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
|
|
590
577
|
|
|
591
578
|
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
592
579
|
/** After reorg, filter the entries that would no longer be valid in the next block, and update
|
|
@@ -695,14 +682,6 @@ public:
|
|
|
695
682
|
* already in it. */
|
|
696
683
|
void CalculateDescendants(txiter it, setEntries& setDescendants) const EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
697
684
|
|
|
698
|
-
/** The minimum fee to get into the mempool, which may itself not be enough
|
|
699
|
-
* for larger-sized transactions.
|
|
700
|
-
* The incrementalRelayFee policy variable is used to bound the time it
|
|
701
|
-
* takes the fee rate to go back down all the way to 0. When the feerate
|
|
702
|
-
* would otherwise be half of this, it is set to 0 instead.
|
|
703
|
-
*/
|
|
704
|
-
CFeeRate GetMinFee(size_t sizelimit) const;
|
|
705
|
-
|
|
706
685
|
/** Remove transactions from the mempool until its dynamic size is <= sizelimit.
|
|
707
686
|
* pvNoSpendsRemaining, if set, will be populated with the list of outpoints
|
|
708
687
|
* which are not in mempool which no longer have any spends in this mempool.
|
|
@@ -22,6 +22,11 @@ protected:
|
|
|
22
22
|
static constexpr int WIDTH = BITS / 8;
|
|
23
23
|
uint8_t m_data[WIDTH];
|
|
24
24
|
public:
|
|
25
|
+
const uint32_t *GetDataPtr() const
|
|
26
|
+
{
|
|
27
|
+
return (const uint32_t *)m_data;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
/* construct 0 value by default */
|
|
26
31
|
constexpr base_blob() : m_data() {}
|
|
27
32
|
|
|
@@ -24,7 +24,8 @@ struct TxInUndoFormatter
|
|
|
24
24
|
{
|
|
25
25
|
template<typename Stream>
|
|
26
26
|
void Ser(Stream &s, const Coin& txout) {
|
|
27
|
-
::Serialize(s, VARINT(txout.nHeight * uint32_t{
|
|
27
|
+
::Serialize(s, VARINT(txout.nHeight * uint32_t{4} + txout.fCoinBase + (txout.fCoinStake ? 2u : 0u)));
|
|
28
|
+
::Serialize(s, VARINT(txout.nTime));
|
|
28
29
|
if (txout.nHeight > 0) {
|
|
29
30
|
// Required to maintain compatibility with older undo format.
|
|
30
31
|
::Serialize(s, (unsigned char)0);
|
|
@@ -36,8 +37,10 @@ struct TxInUndoFormatter
|
|
|
36
37
|
void Unser(Stream &s, Coin& txout) {
|
|
37
38
|
uint32_t nCode = 0;
|
|
38
39
|
::Unserialize(s, VARINT(nCode));
|
|
39
|
-
txout.nHeight = nCode >>
|
|
40
|
+
txout.nHeight = nCode >> 2;
|
|
40
41
|
txout.fCoinBase = nCode & 1;
|
|
42
|
+
txout.fCoinStake = nCode & 2;
|
|
43
|
+
::Unserialize(s, VARINT(txout.nTime));
|
|
41
44
|
if (txout.nHeight > 0) {
|
|
42
45
|
// Old versions stored the version number for the last spend of
|
|
43
46
|
// a transaction's outputs. Non-final spends were indicated with
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**********************************************************************
|
|
2
|
+
* Copyright (c) 2018 Pieter Wuille, Greg Maxwell, Gleb Naumenko *
|
|
3
|
+
* Distributed under the MIT software license, see the accompanying *
|
|
4
|
+
* file LICENSE or http://www.opensource.org/licenses/mit-license.php.*
|
|
5
|
+
**********************************************************************/
|
|
6
|
+
|
|
7
|
+
#ifndef _MINISKETCH_UTIL_H_
|
|
8
|
+
#define _MINISKETCH_UTIL_H_
|
|
9
|
+
|
|
10
|
+
#ifdef MINISKETCH_VERIFY
|
|
11
|
+
#include <stdio.h>
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
#if !defined(__GNUC_PREREQ)
|
|
15
|
+
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
|
|
16
|
+
# define __GNUC_PREREQ(_maj,_min) \
|
|
17
|
+
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
|
|
18
|
+
# else
|
|
19
|
+
# define __GNUC_PREREQ(_maj,_min) 0
|
|
20
|
+
# endif
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
#if __GNUC_PREREQ(3, 0)
|
|
24
|
+
#define EXPECT(x,c) __builtin_expect((x),(c))
|
|
25
|
+
#else
|
|
26
|
+
#define EXPECT(x,c) (x)
|
|
27
|
+
#endif
|
|
28
|
+
|
|
29
|
+
/* Assertion macros */
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Unconditional failure on condition failure.
|
|
33
|
+
* Primarily used in testing harnesses.
|
|
34
|
+
*/
|
|
35
|
+
#define CHECK(cond) do { \
|
|
36
|
+
if (EXPECT(!(cond), 0)) { \
|
|
37
|
+
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, "Check condition failed: " #cond); \
|
|
38
|
+
abort(); \
|
|
39
|
+
} \
|
|
40
|
+
} while(0)
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Check macro that does nothing in normal non-verify builds but crashes in verify builds.
|
|
44
|
+
* This is used to test conditions at runtime that should always be true, but are either
|
|
45
|
+
* expensive to test or in locations where returning on failure would be messy.
|
|
46
|
+
*/
|
|
47
|
+
#ifdef MINISKETCH_VERIFY
|
|
48
|
+
#define CHECK_SAFE(cond) CHECK(cond)
|
|
49
|
+
#else
|
|
50
|
+
#define CHECK_SAFE(cond)
|
|
51
|
+
#endif
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check a condition and return on failure in non-verify builds, crash in verify builds.
|
|
55
|
+
* Used for inexpensive conditions which believed to be always true in locations where
|
|
56
|
+
* a graceful exit is possible.
|
|
57
|
+
*/
|
|
58
|
+
#ifdef MINISKETCH_VERIFY
|
|
59
|
+
#define CHECK_RETURN(cond, rvar) do { \
|
|
60
|
+
if (EXPECT(!(cond), 0)) { \
|
|
61
|
+
fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, "Check condition failed: " #cond); \
|
|
62
|
+
abort(); \
|
|
63
|
+
return rvar; /* Does nothing, but causes compile to warn on incorrect return types. */ \
|
|
64
|
+
} \
|
|
65
|
+
} while(0)
|
|
66
|
+
#else
|
|
67
|
+
#define CHECK_RETURN(cond, rvar) do { \
|
|
68
|
+
if (EXPECT(!(cond), 0)) { \
|
|
69
|
+
return rvar; \
|
|
70
|
+
} \
|
|
71
|
+
} while(0)
|
|
72
|
+
#endif
|
|
73
|
+
|
|
74
|
+
#endif
|
|
@@ -29,8 +29,6 @@ bilingual_str TransactionErrorString(const TransactionError err)
|
|
|
29
29
|
return Untranslated("PSBTs not compatible (different transactions)");
|
|
30
30
|
case TransactionError::SIGHASH_MISMATCH:
|
|
31
31
|
return Untranslated("Specified sighash value does not match value stored in PSBT");
|
|
32
|
-
case TransactionError::MAX_FEE_EXCEEDED:
|
|
33
|
-
return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
|
|
34
32
|
case TransactionError::EXTERNAL_SIGNER_NOT_FOUND:
|
|
35
33
|
return Untranslated("External signer not found");
|
|
36
34
|
case TransactionError::EXTERNAL_SIGNER_FAILED:
|
|
@@ -29,7 +29,6 @@ enum class TransactionError {
|
|
|
29
29
|
INVALID_PSBT,
|
|
30
30
|
PSBT_MISMATCH,
|
|
31
31
|
SIGHASH_MISMATCH,
|
|
32
|
-
MAX_FEE_EXCEEDED,
|
|
33
32
|
EXTERNAL_SIGNER_NOT_FOUND,
|
|
34
33
|
EXTERNAL_SIGNER_FAILED,
|
|
35
34
|
};
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* Text used to signify that a signed message follows and to prevent
|
|
20
20
|
* inadvertently signing a transaction.
|
|
21
21
|
*/
|
|
22
|
-
const std::string MESSAGE_MAGIC = "
|
|
22
|
+
const std::string MESSAGE_MAGIC = "Peercoin Signed Message:\n";
|
|
23
23
|
|
|
24
24
|
MessageVerificationResult MessageVerify(
|
|
25
25
|
const std::string& address,
|
|
@@ -23,7 +23,7 @@ std::string FormatMoney(const CAmount n)
|
|
|
23
23
|
quotient = -quotient;
|
|
24
24
|
remainder = -remainder;
|
|
25
25
|
}
|
|
26
|
-
std::string str = strprintf("%d.%
|
|
26
|
+
std::string str = strprintf("%d.%06d", quotient, remainder);
|
|
27
27
|
|
|
28
28
|
// Right-trim excess zeros before the decimal point:
|
|
29
29
|
int nTrim = 0;
|
|
@@ -426,13 +426,17 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
|
|
|
426
426
|
if (ptr < end && val[ptr] == '.')
|
|
427
427
|
{
|
|
428
428
|
++ptr;
|
|
429
|
+
int peercoin_digits = 6;
|
|
429
430
|
if (ptr < end && IsDigit(val[ptr]))
|
|
430
431
|
{
|
|
431
432
|
while (ptr < end && IsDigit(val[ptr])) {
|
|
432
|
-
if (
|
|
433
|
-
|
|
433
|
+
if (peercoin_digits) {
|
|
434
|
+
if (!ProcessMantissaDigit(val[ptr], mantissa, mantissa_tzeros))
|
|
435
|
+
return false; /* overflow */
|
|
436
|
+
++point_ofs;
|
|
437
|
+
--peercoin_digits;
|
|
438
|
+
}
|
|
434
439
|
++ptr;
|
|
435
|
-
++point_ofs;
|
|
436
440
|
}
|
|
437
441
|
} else return false; /* missing expected digit */
|
|
438
442
|
}
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
// Application startup time (used for uptime calculation)
|
|
91
91
|
const int64_t nStartupTime = GetTime();
|
|
92
92
|
|
|
93
|
-
const char * const BITCOIN_CONF_FILENAME = "
|
|
93
|
+
const char * const BITCOIN_CONF_FILENAME = "peercoin.conf";
|
|
94
94
|
const char * const BITCOIN_SETTINGS_FILENAME = "settings.json";
|
|
95
95
|
|
|
96
96
|
ArgsManager gArgs;
|
|
@@ -775,7 +775,7 @@ static std::string FormatException(const std::exception* pex, const char* pszThr
|
|
|
775
775
|
char pszModule[MAX_PATH] = "";
|
|
776
776
|
GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule));
|
|
777
777
|
#else
|
|
778
|
-
const char* pszModule = "
|
|
778
|
+
const char* pszModule = "peercoin";
|
|
779
779
|
#endif
|
|
780
780
|
if (pex)
|
|
781
781
|
return strprintf(
|
|
@@ -794,12 +794,13 @@ void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
|
|
|
794
794
|
|
|
795
795
|
fs::path GetDefaultDataDir()
|
|
796
796
|
{
|
|
797
|
-
// Windows: C:\
|
|
798
|
-
//
|
|
799
|
-
//
|
|
797
|
+
// Windows < Vista: C:\Documents and Settings\Username\Application Data\Peercoin
|
|
798
|
+
// Windows >= Vista: C:\Users\Username\AppData\Roaming\Peercoin
|
|
799
|
+
// Mac: ~/Library/Application Support/Peercoin
|
|
800
|
+
// Unix: ~/.peercoin
|
|
800
801
|
#ifdef WIN32
|
|
801
802
|
// Windows
|
|
802
|
-
return GetSpecialFolderPath(CSIDL_APPDATA) / "
|
|
803
|
+
return GetSpecialFolderPath(CSIDL_APPDATA) / "Peercoin";
|
|
803
804
|
#else
|
|
804
805
|
fs::path pathRet;
|
|
805
806
|
char* pszHome = getenv("HOME");
|
|
@@ -808,11 +809,11 @@ fs::path GetDefaultDataDir()
|
|
|
808
809
|
else
|
|
809
810
|
pathRet = fs::path(pszHome);
|
|
810
811
|
#ifdef MAC_OSX
|
|
811
|
-
//
|
|
812
|
-
return pathRet / "Library/Application Support/
|
|
812
|
+
// Mac
|
|
813
|
+
return pathRet / "Library/Application Support/Peercoin";
|
|
813
814
|
#else
|
|
814
|
-
// Unix
|
|
815
|
-
return pathRet / ".
|
|
815
|
+
// Unix
|
|
816
|
+
return pathRet / ".peercoin";
|
|
816
817
|
#endif
|
|
817
818
|
#endif
|
|
818
819
|
}
|
|
@@ -16,18 +16,18 @@
|
|
|
16
16
|
#include <consensus/tx_verify.h>
|
|
17
17
|
#include <consensus/validation.h>
|
|
18
18
|
#include <cuckoocache.h>
|
|
19
|
-
#include <deploymentstatus.h>
|
|
20
19
|
#include <flatfile.h>
|
|
21
20
|
#include <hash.h>
|
|
22
21
|
#include <index/blockfilterindex.h>
|
|
22
|
+
#include <index/txindex.h>
|
|
23
23
|
#include <logging.h>
|
|
24
24
|
#include <logging/timer.h>
|
|
25
|
+
#include <net.h>
|
|
25
26
|
#include <node/blockstorage.h>
|
|
26
27
|
#include <node/coinstats.h>
|
|
27
28
|
#include <node/ui_interface.h>
|
|
28
29
|
#include <node/utxo_snapshot.h>
|
|
29
30
|
#include <policy/policy.h>
|
|
30
|
-
#include <policy/rbf.h>
|
|
31
31
|
#include <policy/settings.h>
|
|
32
32
|
#include <pow.h>
|
|
33
33
|
#include <primitives/block.h>
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
#include <util/check.h> // For NDEBUG compile time check
|
|
48
48
|
#include <util/hasher.h>
|
|
49
49
|
#include <util/moneystr.h>
|
|
50
|
-
#include <util/rbf.h>
|
|
51
50
|
#include <util/strencodings.h>
|
|
52
51
|
#include <util/system.h>
|
|
53
52
|
#include <util/trace.h>
|
|
@@ -58,6 +57,10 @@
|
|
|
58
57
|
#include <algorithm>
|
|
59
58
|
#include <numeric>
|
|
60
59
|
#include <optional>
|
|
60
|
+
#include <kernel.h>
|
|
61
|
+
#include <bignum.h>
|
|
62
|
+
#include <wallet/wallet.h>
|
|
63
|
+
|
|
61
64
|
#include <string>
|
|
62
65
|
|
|
63
66
|
#include <boost/algorithm/string/replace.hpp>
|
|
@@ -74,12 +77,8 @@ using node::ReadBlockFromDisk;
|
|
|
74
77
|
using node::SnapshotMetadata;
|
|
75
78
|
using node::UNDOFILE_CHUNK_SIZE;
|
|
76
79
|
using node::UndoReadFromDisk;
|
|
77
|
-
using node::UnlinkPrunedFiles;
|
|
78
|
-
using node::fHavePruned;
|
|
79
80
|
using node::fImporting;
|
|
80
|
-
using node::fPruneMode;
|
|
81
81
|
using node::fReindex;
|
|
82
|
-
using node::nPruneTarget;
|
|
83
82
|
|
|
84
83
|
#define MICRO 0.000001
|
|
85
84
|
#define MILLI 0.001
|
|
@@ -109,8 +108,8 @@ const std::vector<std::string> CHECKLEVEL_DOC {
|
|
|
109
108
|
|
|
110
109
|
bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIndex *pb) const {
|
|
111
110
|
// First sort by most total work, ...
|
|
112
|
-
if (pa->
|
|
113
|
-
if (pa->
|
|
111
|
+
if (pa->nChainTrust > pb->nChainTrust) return false;
|
|
112
|
+
if (pa->nChainTrust < pb->nChainTrust) return true;
|
|
114
113
|
|
|
115
114
|
// ... then by earliest time received, ...
|
|
116
115
|
if (pa->nSequenceId < pb->nSequenceId) return false;
|
|
@@ -125,6 +124,7 @@ bool CBlockIndexWorkComparator::operator()(const CBlockIndex *pa, const CBlockIn
|
|
|
125
124
|
return false;
|
|
126
125
|
}
|
|
127
126
|
|
|
127
|
+
uint256 vStakeSeen[1024];
|
|
128
128
|
/**
|
|
129
129
|
* Mutex to guard access to validation specific variables, such as reading
|
|
130
130
|
* or changing the chainstate.
|
|
@@ -150,8 +150,7 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
|
|
|
150
150
|
uint256 hashAssumeValid;
|
|
151
151
|
arith_uint256 nMinimumChainWork;
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
CTxMemPool mempool;
|
|
155
154
|
CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locator) const
|
|
156
155
|
{
|
|
157
156
|
AssertLockHeld(cs_main);
|
|
@@ -301,18 +300,6 @@ static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, siz
|
|
|
301
300
|
coins_cache.Uncache(removed);
|
|
302
301
|
}
|
|
303
302
|
|
|
304
|
-
static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
|
305
|
-
{
|
|
306
|
-
AssertLockHeld(cs_main);
|
|
307
|
-
if (active_chainstate.IsInitialBlockDownload())
|
|
308
|
-
return false;
|
|
309
|
-
if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime<std::chrono::seconds>() - MAX_FEE_ESTIMATION_TIP_AGE))
|
|
310
|
-
return false;
|
|
311
|
-
if (active_chainstate.m_chain.Height() < pindexBestHeader->nHeight - 1)
|
|
312
|
-
return false;
|
|
313
|
-
return true;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
303
|
void CChainState::MaybeUpdateMempoolForReorg(
|
|
317
304
|
DisconnectedBlockTransactions& disconnectpool,
|
|
318
305
|
bool fAddToMempool)
|
|
@@ -331,7 +318,7 @@ void CChainState::MaybeUpdateMempoolForReorg(
|
|
|
331
318
|
auto it = disconnectpool.queuedTx.get<insertion_order>().rbegin();
|
|
332
319
|
while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) {
|
|
333
320
|
// ignore validation errors in resurrected transactions
|
|
334
|
-
if (!fAddToMempool || (*it)->IsCoinBase() ||
|
|
321
|
+
if (!fAddToMempool || (*it)->IsCoinBase() || (*it)->IsCoinStake() ||
|
|
335
322
|
AcceptToMemoryPool(*this, *it, GetTime(),
|
|
336
323
|
/*bypass_limits=*/true, /*test_accept=*/false).m_result_type !=
|
|
337
324
|
MempoolAcceptResult::ResultType::VALID) {
|
|
@@ -423,7 +410,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, TxValidationS
|
|
|
423
410
|
AssertLockHeld(cs_main);
|
|
424
411
|
AssertLockHeld(pool.cs);
|
|
425
412
|
|
|
426
|
-
assert(!tx.IsCoinBase());
|
|
413
|
+
assert(!tx.IsCoinBase() && !tx.IsCoinStake());
|
|
427
414
|
for (const CTxIn& txin : tx.vin) {
|
|
428
415
|
const Coin& coin = view.AccessCoin(txin.prevout);
|
|
429
416
|
|
|
@@ -598,9 +585,6 @@ private:
|
|
|
598
585
|
// only tests that are fast should be done here (to avoid CPU DoS).
|
|
599
586
|
bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
|
600
587
|
|
|
601
|
-
// Run checks for mempool replace-by-fee.
|
|
602
|
-
bool ReplacementChecks(Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
|
603
|
-
|
|
604
588
|
// Enforce package mempool ancestor/descendant limits (distinct from individual
|
|
605
589
|
// ancestor/descendant limits done in PreChecks).
|
|
606
590
|
bool PackageMempoolChecks(const std::vector<CTransactionRef>& txns,
|
|
@@ -629,22 +613,6 @@ private:
|
|
|
629
613
|
std::map<const uint256, const MempoolAcceptResult>& results)
|
|
630
614
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs);
|
|
631
615
|
|
|
632
|
-
// Compare a package's feerate against minimum allowed.
|
|
633
|
-
bool CheckFeeRate(size_t package_size, CAmount package_fee, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_pool.cs)
|
|
634
|
-
{
|
|
635
|
-
AssertLockHeld(::cs_main);
|
|
636
|
-
AssertLockHeld(m_pool.cs);
|
|
637
|
-
CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetIntArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size);
|
|
638
|
-
if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) {
|
|
639
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee));
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
if (package_fee < ::minRelayTxFee.GetFee(package_size)) {
|
|
643
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size)));
|
|
644
|
-
}
|
|
645
|
-
return true;
|
|
646
|
-
}
|
|
647
|
-
|
|
648
616
|
private:
|
|
649
617
|
CTxMemPool& m_pool;
|
|
650
618
|
CCoinsViewCache m_view;
|
|
@@ -660,9 +628,6 @@ private:
|
|
|
660
628
|
// in-mempool conflicts; see below).
|
|
661
629
|
size_t m_limit_descendants;
|
|
662
630
|
size_t m_limit_descendant_size;
|
|
663
|
-
|
|
664
|
-
/** Whether the transaction(s) would replace any mempool transactions. If so, RBF rules apply. */
|
|
665
|
-
bool m_rbf{false};
|
|
666
631
|
};
|
|
667
632
|
|
|
668
633
|
bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
@@ -675,7 +640,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
675
640
|
|
|
676
641
|
// Copy/alias what we need out of args
|
|
677
642
|
const int64_t nAcceptTime = args.m_accept_time;
|
|
678
|
-
const bool bypass_limits = args.m_bypass_limits;
|
|
679
643
|
std::vector<COutPoint>& coins_to_uncache = args.m_coins_to_uncache;
|
|
680
644
|
|
|
681
645
|
// Alias what we need out of ws
|
|
@@ -685,9 +649,13 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
685
649
|
if (!CheckTransaction(tx, state)) {
|
|
686
650
|
return false; // state filled in by CheckTransaction
|
|
687
651
|
}
|
|
652
|
+
// Time (prevent mempool memory exhaustion attack)
|
|
653
|
+
// moved from CheckTransaction() to here, because it makes no sense to make GetAdjustedTime() a part of the consensus rules - user can set his clock to whatever he wishes.
|
|
654
|
+
if (tx.nTime > GetAdjustedTime() + (IsProtocolV09(GetAdjustedTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9))
|
|
655
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "timestamp-too-far");
|
|
688
656
|
|
|
689
657
|
// Coinbase is only valid in a block, not as a loose transaction
|
|
690
|
-
if (tx.IsCoinBase())
|
|
658
|
+
if (tx.IsCoinBase() || tx.IsCoinStake())
|
|
691
659
|
return state.Invalid(TxValidationResult::TX_CONSENSUS, "coinbase");
|
|
692
660
|
|
|
693
661
|
// Rather not work on nonstandard transactions (unless -testnet/-regtest)
|
|
@@ -722,25 +690,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
722
690
|
{
|
|
723
691
|
const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout);
|
|
724
692
|
if (ptxConflicting) {
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "bip125-replacement-disallowed");
|
|
728
|
-
}
|
|
729
|
-
if (!ws.m_conflicts.count(ptxConflicting->GetHash()))
|
|
730
|
-
{
|
|
731
|
-
// Transactions that don't explicitly signal replaceability are
|
|
732
|
-
// *not* replaceable with the current logic, even if one of their
|
|
733
|
-
// unconfirmed ancestors signals replaceability. This diverges
|
|
734
|
-
// from BIP125's inherited signaling description (see CVE-2021-31876).
|
|
735
|
-
// Applications relying on first-seen mempool behavior should
|
|
736
|
-
// check all unconfirmed ancestors; otherwise an opt-in ancestor
|
|
737
|
-
// might be replaced, causing removal of this descendant.
|
|
738
|
-
if (!SignalsOptInRBF(*ptxConflicting)) {
|
|
739
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
ws.m_conflicts.insert(ptxConflicting->GetHash());
|
|
743
|
-
}
|
|
693
|
+
// Disable replacement feature for now
|
|
694
|
+
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "txn-mempool-conflict");
|
|
744
695
|
}
|
|
745
696
|
}
|
|
746
697
|
|
|
@@ -790,10 +741,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
790
741
|
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
|
|
791
742
|
|
|
792
743
|
// The mempool holds txs for the next block, so pass height+1 to CheckTxInputs
|
|
793
|
-
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees)) {
|
|
744
|
+
if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_chain.Height() + 1, ws.m_base_fees, Params().GetConsensus(), tx.nTime ? tx.nTime : GetAdjustedTime())) {
|
|
794
745
|
return false; // state filled in by CheckTxInputs
|
|
795
746
|
}
|
|
796
747
|
|
|
748
|
+
if (ws.m_base_fees < GetMinFee(tx, tx.nTime ? tx.nTime : GetAdjustedTime()))
|
|
749
|
+
return state.Invalid(TxValidationResult::TX_CONSENSUS, "fee is below minimum");
|
|
797
750
|
// Check for non-standard pay-to-script-hash in inputs
|
|
798
751
|
if (fRequireStandard && !AreInputsStandard(tx, m_view)) {
|
|
799
752
|
return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs");
|
|
@@ -814,7 +767,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
814
767
|
bool fSpendsCoinbase = false;
|
|
815
768
|
for (const CTxIn &txin : tx.vin) {
|
|
816
769
|
const Coin &coin = m_view.AccessCoin(txin.prevout);
|
|
817
|
-
if (coin.IsCoinBase()) {
|
|
770
|
+
if (coin.IsCoinBase() || coin.IsCoinStake()) {
|
|
818
771
|
fSpendsCoinbase = true;
|
|
819
772
|
break;
|
|
820
773
|
}
|
|
@@ -828,46 +781,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
828
781
|
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "bad-txns-too-many-sigops",
|
|
829
782
|
strprintf("%d", nSigOpsCost));
|
|
830
783
|
|
|
831
|
-
// No transactions are allowed below minRelayTxFee except from disconnected
|
|
832
|
-
// blocks
|
|
833
|
-
if (!bypass_limits && !CheckFeeRate(ws.m_vsize, ws.m_modified_fees, state)) return false;
|
|
834
|
-
|
|
835
|
-
ws.m_iters_conflicting = m_pool.GetIterSet(ws.m_conflicts);
|
|
836
|
-
// Calculate in-mempool ancestors, up to a limit.
|
|
837
|
-
if (ws.m_conflicts.size() == 1) {
|
|
838
|
-
// In general, when we receive an RBF transaction with mempool conflicts, we want to know whether we
|
|
839
|
-
// would meet the chain limits after the conflicts have been removed. However, there isn't a practical
|
|
840
|
-
// way to do this short of calculating the ancestor and descendant sets with an overlay cache of
|
|
841
|
-
// changed mempool entries. Due to both implementation and runtime complexity concerns, this isn't
|
|
842
|
-
// very realistic, thus we only ensure a limited set of transactions are RBF'able despite mempool
|
|
843
|
-
// conflicts here. Importantly, we need to ensure that some transactions which were accepted using
|
|
844
|
-
// the below carve-out are able to be RBF'ed, without impacting the security the carve-out provides
|
|
845
|
-
// for off-chain contract systems (see link in the comment below).
|
|
846
|
-
//
|
|
847
|
-
// Specifically, the subset of RBF transactions which we allow despite chain limits are those which
|
|
848
|
-
// conflict directly with exactly one other transaction (but may evict children of said transaction),
|
|
849
|
-
// and which are not adding any new mempool dependencies. Note that the "no new mempool dependencies"
|
|
850
|
-
// check is accomplished later, so we don't bother doing anything about it here, but if BIP 125 is
|
|
851
|
-
// amended, we may need to move that check to here instead of removing it wholesale.
|
|
852
|
-
//
|
|
853
|
-
// Such transactions are clearly not merging any existing packages, so we are only concerned with
|
|
854
|
-
// ensuring that (a) no package is growing past the package size (not count) limits and (b) we are
|
|
855
|
-
// not allowing something to effectively use the (below) carve-out spot when it shouldn't be allowed
|
|
856
|
-
// to.
|
|
857
|
-
//
|
|
858
|
-
// To check these we first check if we meet the RBF criteria, above, and increment the descendant
|
|
859
|
-
// limits by the direct conflict and its descendants (as these are recalculated in
|
|
860
|
-
// CalculateMempoolAncestors by assuming the new transaction being added is a new descendant, with no
|
|
861
|
-
// removals, of each parent's existing dependent set). The ancestor count limits are unmodified (as
|
|
862
|
-
// the ancestor limits should be the same for both our new transaction and any conflicts).
|
|
863
|
-
// We don't bother incrementing m_limit_descendants by the full removal count as that limit never comes
|
|
864
|
-
// into force here (as we're only adding a single transaction).
|
|
865
|
-
assert(ws.m_iters_conflicting.size() == 1);
|
|
866
|
-
CTxMemPool::txiter conflict = *ws.m_iters_conflicting.begin();
|
|
867
|
-
|
|
868
|
-
m_limit_descendants += 1;
|
|
869
|
-
m_limit_descendant_size += conflict->GetSizeWithDescendants();
|
|
870
|
-
}
|
|
871
784
|
|
|
872
785
|
std::string errString;
|
|
873
786
|
if (!m_pool.CalculateMemPoolAncestors(*entry, ws.m_ancestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) {
|
|
@@ -891,63 +804,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
|
|
|
891
804
|
}
|
|
892
805
|
}
|
|
893
806
|
|
|
894
|
-
// A transaction that spends outputs that would be replaced by it is invalid. Now
|
|
895
|
-
// that we have the set of all ancestors we can detect this
|
|
896
|
-
// pathological case by making sure ws.m_conflicts and ws.m_ancestors don't
|
|
897
|
-
// intersect.
|
|
898
|
-
if (const auto err_string{EntriesAndTxidsDisjoint(ws.m_ancestors, ws.m_conflicts, hash)}) {
|
|
899
|
-
// We classify this as a consensus error because a transaction depending on something it
|
|
900
|
-
// conflicts with would be inconsistent.
|
|
901
|
-
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-spends-conflicting-tx", *err_string);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
m_rbf = !ws.m_conflicts.empty();
|
|
905
|
-
return true;
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
bool MemPoolAccept::ReplacementChecks(Workspace& ws)
|
|
909
|
-
{
|
|
910
|
-
AssertLockHeld(cs_main);
|
|
911
|
-
AssertLockHeld(m_pool.cs);
|
|
912
|
-
|
|
913
|
-
const CTransaction& tx = *ws.m_ptx;
|
|
914
|
-
const uint256& hash = ws.m_hash;
|
|
915
|
-
TxValidationState& state = ws.m_state;
|
|
916
|
-
|
|
917
|
-
CFeeRate newFeeRate(ws.m_modified_fees, ws.m_vsize);
|
|
918
|
-
// The replacement transaction must have a higher feerate than its direct conflicts.
|
|
919
|
-
// - The motivation for this check is to ensure that the replacement transaction is preferable for
|
|
920
|
-
// block-inclusion, compared to what would be removed from the mempool.
|
|
921
|
-
// - This logic predates ancestor feerate-based transaction selection, which is why it doesn't
|
|
922
|
-
// consider feerates of descendants.
|
|
923
|
-
// - Note: Ancestor feerate-based transaction selection has made this comparison insufficient to
|
|
924
|
-
// guarantee that this is incentive-compatible for miners, because it is possible for a
|
|
925
|
-
// descendant transaction of a direct conflict to pay a higher feerate than the transaction that
|
|
926
|
-
// might replace them, under these rules.
|
|
927
|
-
if (const auto err_string{PaysMoreThanConflicts(ws.m_iters_conflicting, newFeeRate, hash)}) {
|
|
928
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// Calculate all conflicting entries and enforce BIP125 Rule #5.
|
|
932
|
-
if (const auto err_string{GetEntriesForConflicts(tx, m_pool, ws.m_iters_conflicting, ws.m_all_conflicting)}) {
|
|
933
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
|
|
934
|
-
"too many potential replacements", *err_string);
|
|
935
|
-
}
|
|
936
|
-
// Enforce BIP125 Rule #2.
|
|
937
|
-
if (const auto err_string{HasNoNewUnconfirmed(tx, m_pool, ws.m_iters_conflicting)}) {
|
|
938
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY,
|
|
939
|
-
"replacement-adds-unconfirmed", *err_string);
|
|
940
|
-
}
|
|
941
|
-
// Check if it's economically rational to mine this transaction rather than the ones it
|
|
942
|
-
// replaces and pays for its own relay fees. Enforce BIP125 Rules #3 and #4.
|
|
943
|
-
for (CTxMemPool::txiter it : ws.m_all_conflicting) {
|
|
944
|
-
ws.m_conflicting_fees += it->GetModifiedFee();
|
|
945
|
-
ws.m_conflicting_size += it->GetTxSize();
|
|
946
|
-
}
|
|
947
|
-
if (const auto err_string{PaysForRBF(ws.m_conflicting_fees, ws.m_modified_fees, ws.m_vsize,
|
|
948
|
-
::incrementalRelayFee, hash)}) {
|
|
949
|
-
return state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "insufficient fee", *err_string);
|
|
950
|
-
}
|
|
951
807
|
return true;
|
|
952
808
|
}
|
|
953
809
|
|
|
@@ -977,7 +833,16 @@ bool MemPoolAccept::PolicyScriptChecks(const ATMPArgs& args, Workspace& ws)
|
|
|
977
833
|
const CTransaction& tx = *ws.m_ptx;
|
|
978
834
|
TxValidationState& state = ws.m_state;
|
|
979
835
|
|
|
980
|
-
|
|
836
|
+
unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS;
|
|
837
|
+
|
|
838
|
+
// peercoin: if transaction is after version 0.8 fork, verify SCRIPT_VERIFY_LOW_S
|
|
839
|
+
// ppcTODO move back to policy.h after 0.8 is active
|
|
840
|
+
//if (IsBTC16BIPsEnabled(tx.nTime))
|
|
841
|
+
// scriptVerifyFlags &= SCRIPT_VERIFY_LOW_S;
|
|
842
|
+
|
|
843
|
+
// peercoin allow taproot after fork
|
|
844
|
+
//if (IsProtocolV12(tx.nTime))
|
|
845
|
+
// scriptVerifyFlags &= SCRIPT_VERIFY_TAPROOT;
|
|
981
846
|
|
|
982
847
|
// Check input scripts and signatures.
|
|
983
848
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
@@ -1036,35 +901,14 @@ bool MemPoolAccept::Finalize(const ATMPArgs& args, Workspace& ws)
|
|
|
1036
901
|
{
|
|
1037
902
|
AssertLockHeld(cs_main);
|
|
1038
903
|
AssertLockHeld(m_pool.cs);
|
|
1039
|
-
const CTransaction& tx = *ws.m_ptx;
|
|
1040
904
|
const uint256& hash = ws.m_hash;
|
|
1041
905
|
TxValidationState& state = ws.m_state;
|
|
1042
906
|
const bool bypass_limits = args.m_bypass_limits;
|
|
1043
907
|
|
|
1044
908
|
std::unique_ptr<CTxMemPoolEntry>& entry = ws.m_entry;
|
|
1045
909
|
|
|
1046
|
-
// Remove conflicting transactions from the mempool
|
|
1047
|
-
for (CTxMemPool::txiter it : ws.m_all_conflicting)
|
|
1048
|
-
{
|
|
1049
|
-
LogPrint(BCLog::MEMPOOL, "replacing tx %s with %s for %s additional fees, %d delta bytes\n",
|
|
1050
|
-
it->GetTx().GetHash().ToString(),
|
|
1051
|
-
hash.ToString(),
|
|
1052
|
-
FormatMoney(ws.m_modified_fees - ws.m_conflicting_fees),
|
|
1053
|
-
(int)entry->GetTxSize() - (int)ws.m_conflicting_size);
|
|
1054
|
-
ws.m_replaced_transactions.push_back(it->GetSharedTx());
|
|
1055
|
-
}
|
|
1056
|
-
m_pool.RemoveStaged(ws.m_all_conflicting, false, MemPoolRemovalReason::REPLACED);
|
|
1057
|
-
|
|
1058
|
-
// This transaction should only count for fee estimation if:
|
|
1059
|
-
// - it's not being re-added during a reorg which bypasses typical mempool fee limits
|
|
1060
|
-
// - the node is not behind
|
|
1061
|
-
// - the transaction is not dependent on any other transactions in the mempool
|
|
1062
|
-
// - it's not part of a package. Since package relay is not currently supported, this
|
|
1063
|
-
// transaction has not necessarily been accepted to miners' mempools.
|
|
1064
|
-
bool validForFeeEstimation = !bypass_limits && !args.m_package_submission && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx);
|
|
1065
|
-
|
|
1066
910
|
// Store transaction in memory
|
|
1067
|
-
m_pool.addUnchecked(*entry, ws.m_ancestors
|
|
911
|
+
m_pool.addUnchecked(*entry, ws.m_ancestors);
|
|
1068
912
|
|
|
1069
913
|
// trim mempool and check if tx was trimmed
|
|
1070
914
|
// If we are validating a package, don't trim here because we could evict a previous transaction
|
|
@@ -1162,8 +1006,6 @@ MempoolAcceptResult MemPoolAccept::AcceptSingleTransaction(const CTransactionRef
|
|
|
1162
1006
|
|
|
1163
1007
|
if (!PreChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
|
|
1164
1008
|
|
|
1165
|
-
if (m_rbf && !ReplacementChecks(ws)) return MempoolAcceptResult::Failure(ws.m_state);
|
|
1166
|
-
|
|
1167
1009
|
// Perform the inexpensive checks first and avoid hashing and signature verification unless
|
|
1168
1010
|
// those checks pass, to mitigate CPU exhaustion denial-of-service attacks.
|
|
1169
1011
|
if (!PolicyScriptChecks(args, ws)) return MempoolAcceptResult::Failure(ws.m_state);
|
|
@@ -1414,16 +1256,67 @@ PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTx
|
|
|
1414
1256
|
return result;
|
|
1415
1257
|
}
|
|
1416
1258
|
|
|
1417
|
-
|
|
1259
|
+
int64_t GetProofOfWorkReward(unsigned int nBits, uint32_t nTime)
|
|
1418
1260
|
{
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
//
|
|
1426
|
-
|
|
1261
|
+
CBigNum bnSubsidyLimit = MAX_MINT_PROOF_OF_WORK;
|
|
1262
|
+
CBigNum bnTarget;
|
|
1263
|
+
bnTarget.SetCompact(nBits);
|
|
1264
|
+
CBigNum bnTargetLimit(Params().GetConsensus().powLimit);
|
|
1265
|
+
bnTargetLimit.SetCompact(bnTargetLimit.GetCompact());
|
|
1266
|
+
|
|
1267
|
+
// peercoin: subsidy is cut in half every 16x multiply of difficulty
|
|
1268
|
+
// A reasonably continuous curve is used to avoid shock to market
|
|
1269
|
+
// (nSubsidyLimit / nSubsidy) ** 4 == bnProofOfWorkLimit / bnTarget
|
|
1270
|
+
CBigNum bnLowerBound = CENT;
|
|
1271
|
+
CBigNum bnUpperBound = bnSubsidyLimit;
|
|
1272
|
+
while (bnLowerBound + CENT <= bnUpperBound)
|
|
1273
|
+
{
|
|
1274
|
+
CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2;
|
|
1275
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
1276
|
+
LogPrintf("%s: lower=%lld upper=%lld mid=%lld\n", __func__, bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64());
|
|
1277
|
+
if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget)
|
|
1278
|
+
bnUpperBound = bnMidValue;
|
|
1279
|
+
else
|
|
1280
|
+
bnLowerBound = bnMidValue;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
int64_t nSubsidy = bnUpperBound.getuint64();
|
|
1284
|
+
nSubsidy = (nSubsidy / CENT) * CENT;
|
|
1285
|
+
|
|
1286
|
+
nSubsidy = std::min(nSubsidy, IsProtocolV10(nTime) ? MAX_MINT_PROOF_OF_WORK_V10 : MAX_MINT_PROOF_OF_WORK);
|
|
1287
|
+
|
|
1288
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
1289
|
+
LogPrintf("%s: create=%s nBits=0x%08x nSubsidy=%lld\n", __func__, FormatMoney(nSubsidy), nBits, nSubsidy);
|
|
1290
|
+
|
|
1291
|
+
return nSubsidy;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// peercoin: miner's coin stake is rewarded based on coin age spent (coin-days)
|
|
1295
|
+
int64_t GetProofOfStakeReward(int64_t nCoinAge, uint32_t nTime, uint64_t nMoneySupply)
|
|
1296
|
+
{
|
|
1297
|
+
static int64_t nRewardCoinYear = CENT; // creation amount per coin-year
|
|
1298
|
+
int64_t nSubsidy = nCoinAge * 33 / (365 * 33 + 8) * nRewardCoinYear;
|
|
1299
|
+
|
|
1300
|
+
if (IsProtocolV09(nTime)) {
|
|
1301
|
+
// rfc18
|
|
1302
|
+
// YearlyBlocks = ((365 * 33 + 8) / 33) * 1440 / 10
|
|
1303
|
+
// some efforts not to lose precision
|
|
1304
|
+
CBigNum bnInflationAdjustment = nMoneySupply;
|
|
1305
|
+
bnInflationAdjustment *= 25 * 33;
|
|
1306
|
+
bnInflationAdjustment /= 10000 * 144;
|
|
1307
|
+
bnInflationAdjustment /= (365 * 33 + 8);
|
|
1308
|
+
|
|
1309
|
+
uint64_t nInflationAdjustment = bnInflationAdjustment.getuint64();
|
|
1310
|
+
uint64_t nSubsidyNew = (nSubsidy * 3) + nInflationAdjustment;
|
|
1311
|
+
|
|
1312
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
1313
|
+
LogPrintf("%s: money supply %ld, inflation adjustment %f, old subsidy %ld, new subsidy %ld\n", __func__, nMoneySupply, nInflationAdjustment/1000000.0, nSubsidy, nSubsidyNew);
|
|
1314
|
+
|
|
1315
|
+
nSubsidy = nSubsidyNew;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
1319
|
+
LogPrintf("%s: create=%s nCoinAge=%lld\n", __func__, FormatMoney(nSubsidy), nCoinAge);
|
|
1427
1320
|
return nSubsidy;
|
|
1428
1321
|
}
|
|
1429
1322
|
|
|
@@ -1492,7 +1385,7 @@ bool CChainState::IsInitialBlockDownload() const
|
|
|
1492
1385
|
return true;
|
|
1493
1386
|
if (m_chain.Tip() == nullptr)
|
|
1494
1387
|
return true;
|
|
1495
|
-
if (m_chain.Tip()->
|
|
1388
|
+
if (m_chain.Tip()->nChainTrust < nMinimumChainWork)
|
|
1496
1389
|
return true;
|
|
1497
1390
|
if (m_chain.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))
|
|
1498
1391
|
return true;
|
|
@@ -1501,9 +1394,11 @@ bool CChainState::IsInitialBlockDownload() const
|
|
|
1501
1394
|
return false;
|
|
1502
1395
|
}
|
|
1503
1396
|
|
|
1504
|
-
|
|
1397
|
+
|
|
1398
|
+
void AlertNotify(const std::string& strMessage, bool fUpdateUI)
|
|
1505
1399
|
{
|
|
1506
|
-
|
|
1400
|
+
if (fUpdateUI)
|
|
1401
|
+
uiInterface.NotifyAlertChanged(uint256(), CT_UPDATED); // peercoin: we are using arguments that will have no effects in updateAlert()
|
|
1507
1402
|
#if HAVE_SYSTEM
|
|
1508
1403
|
std::string strCmd = gArgs.GetArg("-alertnotify", "");
|
|
1509
1404
|
if (strCmd.empty()) return;
|
|
@@ -1531,7 +1426,7 @@ void CChainState::CheckForkWarningConditions()
|
|
|
1531
1426
|
return;
|
|
1532
1427
|
}
|
|
1533
1428
|
|
|
1534
|
-
if (m_chainman.m_best_invalid && m_chainman.m_best_invalid->
|
|
1429
|
+
if (m_chainman.m_best_invalid && m_chainman.m_best_invalid->nChainTrust > m_chain.Tip()->nChainTrust + (GetBlockTrust(*m_chain.Tip()) * 6)) {
|
|
1535
1430
|
LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__);
|
|
1536
1431
|
SetfLargeWorkInvalidChainFound(true);
|
|
1537
1432
|
} else {
|
|
@@ -1543,21 +1438,25 @@ void CChainState::CheckForkWarningConditions()
|
|
|
1543
1438
|
void CChainState::InvalidChainFound(CBlockIndex* pindexNew)
|
|
1544
1439
|
{
|
|
1545
1440
|
AssertLockHeld(cs_main);
|
|
1546
|
-
if (!m_chainman.m_best_invalid || pindexNew->
|
|
1441
|
+
if (!m_chainman.m_best_invalid || pindexNew->nChainTrust > m_chainman.m_best_invalid->nChainTrust) {
|
|
1547
1442
|
m_chainman.m_best_invalid = pindexNew;
|
|
1548
1443
|
}
|
|
1549
1444
|
if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) {
|
|
1550
1445
|
pindexBestHeader = m_chain.Tip();
|
|
1551
1446
|
}
|
|
1552
1447
|
|
|
1553
|
-
LogPrintf("%s: invalid block=%s height=%d
|
|
1448
|
+
LogPrintf("%s: invalid block=%s height=%d log2_trust=%.8g moneysupply=%s date=%s moneysupply=%s\n", __func__,
|
|
1554
1449
|
pindexNew->GetBlockHash().ToString(), pindexNew->nHeight,
|
|
1555
|
-
log(pindexNew->
|
|
1450
|
+
log(pindexNew->nChainTrust.getdouble())/log(2.0),
|
|
1451
|
+
FormatMoney(m_chain.Tip()->nMoneySupply),
|
|
1452
|
+
FormatISO8601DateTime(pindexNew->GetBlockTime()),
|
|
1453
|
+
FormatMoney(pindexNew->nMoneySupply));
|
|
1556
1454
|
CBlockIndex *tip = m_chain.Tip();
|
|
1557
1455
|
assert (tip);
|
|
1558
|
-
LogPrintf("%s: current best=%s height=%d
|
|
1559
|
-
tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->
|
|
1560
|
-
|
|
1456
|
+
LogPrintf("%s: current best=%s height=%d log2_trust=%.8g moneysupply=%s date=%s moneysupply=%s\n", __func__,
|
|
1457
|
+
tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->nChainTrust.getdouble())/log(2.0),
|
|
1458
|
+
FormatMoney(tip->nMoneySupply),
|
|
1459
|
+
FormatISO8601DateTime(tip->GetBlockTime()), FormatMoney(pindexNew->nMoneySupply));
|
|
1561
1460
|
CheckForkWarningConditions();
|
|
1562
1461
|
}
|
|
1563
1462
|
|
|
@@ -1575,7 +1474,7 @@ void CChainState::InvalidBlockFound(CBlockIndex* pindex, const BlockValidationSt
|
|
|
1575
1474
|
}
|
|
1576
1475
|
}
|
|
1577
1476
|
|
|
1578
|
-
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight)
|
|
1477
|
+
void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight, bool skipZeroValue)
|
|
1579
1478
|
{
|
|
1580
1479
|
// mark inputs spent
|
|
1581
1480
|
if (!tx.IsCoinBase()) {
|
|
@@ -1587,7 +1486,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund
|
|
|
1587
1486
|
}
|
|
1588
1487
|
}
|
|
1589
1488
|
// add outputs
|
|
1590
|
-
AddCoins(inputs, tx, nHeight);
|
|
1489
|
+
AddCoins(inputs, tx, nHeight, false, skipZeroValue);
|
|
1591
1490
|
}
|
|
1592
1491
|
|
|
1593
1492
|
bool CScriptCheck::operator()() {
|
|
@@ -1749,6 +1648,8 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
|
|
|
1749
1648
|
if (!alternate.IsSpent()) {
|
|
1750
1649
|
undo.nHeight = alternate.nHeight;
|
|
1751
1650
|
undo.fCoinBase = alternate.fCoinBase;
|
|
1651
|
+
undo.fCoinStake = alternate.fCoinStake; // peercoin
|
|
1652
|
+
undo.nTime = alternate.nTime; // peercoin
|
|
1752
1653
|
} else {
|
|
1753
1654
|
return DISCONNECT_FAILED; // adding output for transaction without known metadata
|
|
1754
1655
|
}
|
|
@@ -1758,7 +1659,7 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
|
|
|
1758
1659
|
// already checked whether an unspent coin exists above using HaveCoin, so
|
|
1759
1660
|
// we don't need to guess. When fClean is false, an unspent coin already
|
|
1760
1661
|
// existed and it is an overwrite.
|
|
1761
|
-
view.AddCoin(out, std::move(undo), !fClean);
|
|
1662
|
+
view.AddCoin(out, std::move(undo), !fClean, false);
|
|
1762
1663
|
|
|
1763
1664
|
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
|
1764
1665
|
}
|
|
@@ -1786,15 +1687,18 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
|
|
|
1786
1687
|
const CTransaction &tx = *(block.vtx[i]);
|
|
1787
1688
|
uint256 hash = tx.GetHash();
|
|
1788
1689
|
bool is_coinbase = tx.IsCoinBase();
|
|
1690
|
+
bool is_coinstake = tx.IsCoinStake();
|
|
1789
1691
|
|
|
1790
1692
|
// Check that all outputs are available and match the outputs in the block itself
|
|
1791
1693
|
// exactly.
|
|
1792
1694
|
for (size_t o = 0; o < tx.vout.size(); o++) {
|
|
1793
1695
|
if (!tx.vout[o].scriptPubKey.IsUnspendable()) {
|
|
1696
|
+
if (IsProtocolV12(pindex) && !tx.vout[o].nValue)
|
|
1697
|
+
continue;
|
|
1794
1698
|
COutPoint out(hash, o);
|
|
1795
1699
|
Coin coin;
|
|
1796
1700
|
bool is_spent = view.SpendCoin(out, &coin);
|
|
1797
|
-
if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) {
|
|
1701
|
+
if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase || is_coinstake != coin.fCoinStake) {
|
|
1798
1702
|
fClean = false; // transaction output mismatch
|
|
1799
1703
|
}
|
|
1800
1704
|
}
|
|
@@ -1836,33 +1740,6 @@ void StopScriptCheckWorkerThreads()
|
|
|
1836
1740
|
scriptcheckqueue.StopWorkerThreads();
|
|
1837
1741
|
}
|
|
1838
1742
|
|
|
1839
|
-
/**
|
|
1840
|
-
* Threshold condition checker that triggers when unknown versionbits are seen on the network.
|
|
1841
|
-
*/
|
|
1842
|
-
class WarningBitsConditionChecker : public AbstractThresholdConditionChecker
|
|
1843
|
-
{
|
|
1844
|
-
private:
|
|
1845
|
-
int bit;
|
|
1846
|
-
|
|
1847
|
-
public:
|
|
1848
|
-
explicit WarningBitsConditionChecker(int bitIn) : bit(bitIn) {}
|
|
1849
|
-
|
|
1850
|
-
int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
|
|
1851
|
-
int64_t EndTime(const Consensus::Params& params) const override { return std::numeric_limits<int64_t>::max(); }
|
|
1852
|
-
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
|
|
1853
|
-
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
|
|
1854
|
-
|
|
1855
|
-
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
|
|
1856
|
-
{
|
|
1857
|
-
return pindex->nHeight >= params.MinBIP9WarningHeight &&
|
|
1858
|
-
((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) &&
|
|
1859
|
-
((pindex->nVersion >> bit) & 1) != 0 &&
|
|
1860
|
-
((g_versionbitscache.ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0;
|
|
1861
|
-
}
|
|
1862
|
-
};
|
|
1863
|
-
|
|
1864
|
-
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS] GUARDED_BY(cs_main);
|
|
1865
|
-
|
|
1866
1743
|
static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams)
|
|
1867
1744
|
{
|
|
1868
1745
|
unsigned int flags = SCRIPT_VERIFY_NONE;
|
|
@@ -1881,30 +1758,26 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens
|
|
|
1881
1758
|
}
|
|
1882
1759
|
|
|
1883
1760
|
// Enforce the DERSIG (BIP66) rule
|
|
1884
|
-
if (
|
|
1761
|
+
if (pindex->pprev && IsBTC16BIPsEnabled(pindex->pprev->nTime)) {
|
|
1885
1762
|
flags |= SCRIPT_VERIFY_DERSIG;
|
|
1886
1763
|
}
|
|
1887
1764
|
|
|
1888
1765
|
// Enforce CHECKLOCKTIMEVERIFY (BIP65)
|
|
1889
|
-
if (
|
|
1766
|
+
if (IsProtocolV06(pindex->pprev)) {
|
|
1890
1767
|
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY;
|
|
1891
1768
|
}
|
|
1892
1769
|
|
|
1893
|
-
// Enforce
|
|
1894
|
-
|
|
1895
|
-
|
|
1770
|
+
// Enforce BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY)
|
|
1771
|
+
// Enforce BIP147 NULLDUMMY (activated simultaneously with segwit)
|
|
1772
|
+
if (pindex->pprev && IsBTC16BIPsEnabled(pindex->pprev->nTime)) {
|
|
1773
|
+
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY | SCRIPT_VERIFY_NULLDUMMY;
|
|
1896
1774
|
}
|
|
1897
1775
|
|
|
1898
1776
|
// Enforce Taproot (BIP340-BIP342)
|
|
1899
|
-
if (
|
|
1777
|
+
if (pindex->pprev && IsProtocolV12(pindex->pprev)) {
|
|
1900
1778
|
flags |= SCRIPT_VERIFY_TAPROOT;
|
|
1901
1779
|
}
|
|
1902
1780
|
|
|
1903
|
-
// Enforce BIP147 NULLDUMMY (activated simultaneously with segwit)
|
|
1904
|
-
if (DeploymentActiveAt(*pindex, consensusparams, Consensus::DEPLOYMENT_SEGWIT)) {
|
|
1905
|
-
flags |= SCRIPT_VERIFY_NULLDUMMY;
|
|
1906
|
-
}
|
|
1907
|
-
|
|
1908
1781
|
return flags;
|
|
1909
1782
|
}
|
|
1910
1783
|
|
|
@@ -1918,6 +1791,80 @@ static int64_t nTimeIndex = 0;
|
|
|
1918
1791
|
static int64_t nTimeTotal = 0;
|
|
1919
1792
|
static int64_t nBlocksTotal = 0;
|
|
1920
1793
|
|
|
1794
|
+
// These checks can only be done when all previous block have been added.
|
|
1795
|
+
bool PeercoinContextualBlockChecks(const CBlock& block, BlockValidationState& state, CBlockIndex* pindex, bool fJustCheck, CChainState& chainstate)
|
|
1796
|
+
{
|
|
1797
|
+
uint256 hashProofOfStake = uint256();
|
|
1798
|
+
// peercoin: verify hash target and signature of coinstake tx
|
|
1799
|
+
if (block.IsProofOfStake() && !CheckProofOfStake(state, pindex->pprev, block.vtx[1], block.nBits, hashProofOfStake, block.vtx[1]->nTime ? block.vtx[1]->nTime : block.nTime, chainstate)) {
|
|
1800
|
+
LogPrintf("WARNING: %s: check proof-of-stake failed for block %s\n", __func__, block.GetHash().ToString());
|
|
1801
|
+
return false; // do not error here as we expect this during initial block download
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// peercoin: check for duplicity of stake
|
|
1805
|
+
if (block.IsProofOfStake()) {
|
|
1806
|
+
std::pair<COutPoint, unsigned int> proofOfStake = block.GetProofOfStake();
|
|
1807
|
+
if (pindex->IsProofOfStake() && proofOfStake.first == pindex->prevoutStake) {
|
|
1808
|
+
LogPrintf("WARNING: %s: duplicate proof-of-stake in block %s, invalidating tip\n", __func__, block.GetHash().ToString());
|
|
1809
|
+
chainstate.InvalidateBlock(state, pindex);
|
|
1810
|
+
return error("ConnectBlock() : Duplicate coinstake found");
|
|
1811
|
+
} else if (setStakeSeen.count(proofOfStake)) {
|
|
1812
|
+
LogPrintf("WARNING: %s: duplicate proof-of-stake in block %s\n", __func__, block.GetHash().ToString());
|
|
1813
|
+
return error("ConnectBlock() : Duplicate coinstake found");
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// peercoin: compute stake entropy bit for stake modifier
|
|
1818
|
+
unsigned int nEntropyBit = GetStakeEntropyBit(block);
|
|
1819
|
+
|
|
1820
|
+
// peercoin: compute stake modifier
|
|
1821
|
+
uint64_t nStakeModifier = 0;
|
|
1822
|
+
bool fGeneratedStakeModifier = false;
|
|
1823
|
+
if (!ComputeNextStakeModifier(pindex, nStakeModifier, fGeneratedStakeModifier, chainstate))
|
|
1824
|
+
return error("ConnectBlock() : ComputeNextStakeModifier() failed");
|
|
1825
|
+
|
|
1826
|
+
// compute nStakeModifierChecksum begin
|
|
1827
|
+
unsigned int nFlagsBackup = pindex->nFlags;
|
|
1828
|
+
uint64_t nStakeModifierBackup = pindex->nStakeModifier;
|
|
1829
|
+
uint256 hashProofOfStakeBackup = pindex->hashProofOfStake;
|
|
1830
|
+
|
|
1831
|
+
// set necessary pindex fields
|
|
1832
|
+
if (!pindex->SetStakeEntropyBit(nEntropyBit))
|
|
1833
|
+
return error("ConnectBlock() : SetStakeEntropyBit() failed");
|
|
1834
|
+
pindex->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
|
|
1835
|
+
pindex->hashProofOfStake = hashProofOfStake;
|
|
1836
|
+
|
|
1837
|
+
unsigned int nStakeModifierChecksum = GetStakeModifierChecksum(pindex);
|
|
1838
|
+
|
|
1839
|
+
// undo pindex fields
|
|
1840
|
+
pindex->nFlags = nFlagsBackup;
|
|
1841
|
+
pindex->nStakeModifier = nStakeModifierBackup;
|
|
1842
|
+
pindex->hashProofOfStake = hashProofOfStakeBackup;
|
|
1843
|
+
// compute nStakeModifierChecksum end
|
|
1844
|
+
|
|
1845
|
+
if (!CheckStakeModifierCheckpoints(pindex->nHeight, nStakeModifierChecksum))
|
|
1846
|
+
return error("ConnectBlock() : Rejected by stake modifier checkpoint height=%d, modifier=0x%016llx", pindex->nHeight, nStakeModifier);
|
|
1847
|
+
|
|
1848
|
+
if (fJustCheck)
|
|
1849
|
+
return true;
|
|
1850
|
+
|
|
1851
|
+
// write everything to index
|
|
1852
|
+
if (block.IsProofOfStake())
|
|
1853
|
+
{
|
|
1854
|
+
pindex->prevoutStake = block.vtx[1]->vin[0].prevout;
|
|
1855
|
+
pindex->nStakeTime = block.vtx[1]->nTime;
|
|
1856
|
+
pindex->hashProofOfStake = hashProofOfStake;
|
|
1857
|
+
setStakeSeen.insert(std::make_pair(pindex->prevoutStake, pindex->nTime));
|
|
1858
|
+
}
|
|
1859
|
+
if (!pindex->SetStakeEntropyBit(nEntropyBit))
|
|
1860
|
+
return error("ConnectBlock() : SetStakeEntropyBit() failed");
|
|
1861
|
+
pindex->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
|
|
1862
|
+
pindex->nStakeModifierChecksum = nStakeModifierChecksum;
|
|
1863
|
+
chainstate.m_blockman.m_dirty_blockindex.insert(pindex); // queue a write to disk
|
|
1864
|
+
|
|
1865
|
+
return true;
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1921
1868
|
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
|
|
1922
1869
|
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
|
|
1923
1870
|
* can fail if those validity checks fail (among other reasons). */
|
|
@@ -1932,6 +1879,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
1932
1879
|
|
|
1933
1880
|
int64_t nTimeStart = GetTimeMicros();
|
|
1934
1881
|
|
|
1882
|
+
if (pindex->nStakeModifier == 0 && pindex->nStakeModifierChecksum == 0 && !PeercoinContextualBlockChecks(block, state, pindex, fJustCheck, m_chainman.ActiveChainstate()))
|
|
1883
|
+
return error("%s: failed PoS check %s", __func__, state.ToString());
|
|
1884
|
+
|
|
1935
1885
|
// Check it again in case a previous version let a bad block in
|
|
1936
1886
|
// NOTE: We don't currently (re-)invoke ContextualCheckBlock() or
|
|
1937
1887
|
// ContextualCheckBlockHeader() here. This means that if we add a new
|
|
@@ -1980,7 +1930,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
1980
1930
|
if (it != m_blockman.m_block_index.end()) {
|
|
1981
1931
|
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
|
|
1982
1932
|
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
|
|
1983
|
-
pindexBestHeader->
|
|
1933
|
+
pindexBestHeader->nChainTrust >= nMinimumChainWork) {
|
|
1984
1934
|
// This block is a member of the assumed verified chain and an ancestor of the best header.
|
|
1985
1935
|
// Script verification is skipped when connecting blocks under the
|
|
1986
1936
|
// assumevalid block. Assuming the assumevalid block is valid this
|
|
@@ -2013,8 +1963,7 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2013
1963
|
// Now that the whole chain is irreversibly beyond that time it is applied to all blocks except the
|
|
2014
1964
|
// two in the chain that violate it. This prevents exploiting the issue against nodes during their
|
|
2015
1965
|
// initial block download.
|
|
2016
|
-
bool fEnforceBIP30 = !
|
|
2017
|
-
(pindex->nHeight==91880 && pindex->GetBlockHash() == uint256S("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721")));
|
|
1966
|
+
bool fEnforceBIP30 = (!pindex->phashBlock); // Enforce on CreateNewBlock invocations which don't have a hash.
|
|
2018
1967
|
|
|
2019
1968
|
// Once BIP34 activated it was not possible to create new duplicate coinbases and thus other than starting
|
|
2020
1969
|
// with the 2 existing duplicate coinbase pairs, not possible to create overwriting txs. But by the
|
|
@@ -2090,9 +2039,9 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2090
2039
|
}
|
|
2091
2040
|
}
|
|
2092
2041
|
|
|
2093
|
-
// Enforce BIP68 (sequence locks)
|
|
2042
|
+
// Enforce BIP68 (sequence locks) and BIP112 (CHECKSEQUENCEVERIFY)
|
|
2094
2043
|
int nLockTimeFlags = 0;
|
|
2095
|
-
if (
|
|
2044
|
+
if (pindex->pprev && IsBTC16BIPsEnabled(pindex->pprev->nTime)) {
|
|
2096
2045
|
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE;
|
|
2097
2046
|
}
|
|
2098
2047
|
|
|
@@ -2114,6 +2063,8 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2114
2063
|
|
|
2115
2064
|
std::vector<int> prevheights;
|
|
2116
2065
|
CAmount nFees = 0;
|
|
2066
|
+
int64_t nValueIn = 0;
|
|
2067
|
+
int64_t nValueOut = 0;
|
|
2117
2068
|
int nInputs = 0;
|
|
2118
2069
|
int64_t nSigOpsCost = 0;
|
|
2119
2070
|
blockundo.vtxundo.reserve(block.vtx.size() - 1);
|
|
@@ -2123,17 +2074,23 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2123
2074
|
|
|
2124
2075
|
nInputs += tx.vin.size();
|
|
2125
2076
|
|
|
2126
|
-
if (
|
|
2077
|
+
if (tx.IsCoinBase())
|
|
2078
|
+
nValueOut += tx.GetValueOut();
|
|
2079
|
+
else
|
|
2127
2080
|
{
|
|
2128
2081
|
CAmount txfee = 0;
|
|
2129
2082
|
TxValidationState tx_state;
|
|
2130
|
-
if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, txfee)) {
|
|
2083
|
+
if (!Consensus::CheckTxInputs(tx, tx_state, view, pindex->nHeight, txfee, Params().GetConsensus(), tx.nTime ? tx.nTime : block.nTime, (pindex->pprev? pindex->pprev->nMoneySupply : 0))) {
|
|
2131
2084
|
// Any transaction validation failure in ConnectBlock is a block consensus failure
|
|
2132
2085
|
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS,
|
|
2133
2086
|
tx_state.GetRejectReason(), tx_state.GetDebugMessage());
|
|
2134
2087
|
return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), state.ToString());
|
|
2135
2088
|
}
|
|
2136
|
-
|
|
2089
|
+
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
|
2090
|
+
nValueIn += view.AccessCoin(tx.vin[i].prevout).out.nValue;
|
|
2091
|
+
nValueOut += tx.GetValueOut();
|
|
2092
|
+
if (!tx.IsCoinStake())
|
|
2093
|
+
nFees += txfee;
|
|
2137
2094
|
if (!MoneyRange(nFees)) {
|
|
2138
2095
|
LogPrintf("ERROR: %s: accumulated fee in the block out of range.\n", __func__);
|
|
2139
2096
|
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-txns-accumulated-fee-outofrange");
|
|
@@ -2182,16 +2139,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2182
2139
|
if (i > 0) {
|
|
2183
2140
|
blockundo.vtxundo.push_back(CTxUndo());
|
|
2184
2141
|
}
|
|
2185
|
-
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight);
|
|
2142
|
+
UpdateCoins(tx, view, i == 0 ? undoDummy : blockundo.vtxundo.back(), pindex->nHeight, IsProtocolV12(pindex));
|
|
2186
2143
|
}
|
|
2187
2144
|
int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2;
|
|
2188
2145
|
LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal);
|
|
2189
2146
|
|
|
2190
|
-
|
|
2191
|
-
if (block.vtx[0]->GetValueOut() > blockReward) {
|
|
2192
|
-
LogPrintf("ERROR: ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)\n", block.vtx[0]->GetValueOut(), blockReward);
|
|
2193
|
-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-amount");
|
|
2194
|
-
}
|
|
2147
|
+
// peercoin: coinbase reward check relocated to CheckBlock()
|
|
2195
2148
|
|
|
2196
2149
|
if (!control.Wait()) {
|
|
2197
2150
|
LogPrintf("ERROR: %s: CheckQueue failed\n", __func__);
|
|
@@ -2203,6 +2156,15 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state,
|
|
|
2203
2156
|
if (fJustCheck)
|
|
2204
2157
|
return true;
|
|
2205
2158
|
|
|
2159
|
+
// peercoin: track money supply and mint amount info
|
|
2160
|
+
pindex->nMint = nValueOut - nValueIn + nFees;
|
|
2161
|
+
pindex->nMoneySupply = (pindex->pprev? pindex->pprev->nMoneySupply : 0) + nValueOut - nValueIn;
|
|
2162
|
+
|
|
2163
|
+
// peercoin: fees are not collected by miners as in bitcoin
|
|
2164
|
+
// peercoin: fees are destroyed to compensate the entire network
|
|
2165
|
+
if (gArgs.GetBoolArg("-printcreation", false))
|
|
2166
|
+
LogPrintf("%s: destroy=%s nFees=%lld\n", __func__, FormatMoney(nFees), nFees);
|
|
2167
|
+
|
|
2206
2168
|
if (!m_blockman.WriteUndoDataForBlock(blockundo, state, pindex, m_params)) {
|
|
2207
2169
|
return false;
|
|
2208
2170
|
}
|
|
@@ -2272,7 +2234,6 @@ bool CChainState::FlushStateToDisk(
|
|
|
2272
2234
|
assert(this->CanFlushToDisk());
|
|
2273
2235
|
static std::chrono::microseconds nLastWrite{0};
|
|
2274
2236
|
static std::chrono::microseconds nLastFlush{0};
|
|
2275
|
-
std::set<int> setFilesToPrune;
|
|
2276
2237
|
bool full_flush_completed = false;
|
|
2277
2238
|
|
|
2278
2239
|
const size_t coins_count = CoinsTip().GetCacheSize();
|
|
@@ -2280,12 +2241,10 @@ bool CChainState::FlushStateToDisk(
|
|
|
2280
2241
|
|
|
2281
2242
|
try {
|
|
2282
2243
|
{
|
|
2283
|
-
bool fFlushForPrune = false;
|
|
2284
2244
|
bool fDoFullFlush = false;
|
|
2285
2245
|
|
|
2286
2246
|
CoinsCacheSizeState cache_state = GetCoinsCacheSizeState();
|
|
2287
2247
|
LOCK(m_blockman.cs_LastBlockFile);
|
|
2288
|
-
if (fPruneMode && (m_blockman.m_check_for_pruning || nManualPruneHeight > 0) && !fReindex) {
|
|
2289
2248
|
// make sure we don't prune above the blockfilterindexes bestblocks
|
|
2290
2249
|
// pruning is height-based
|
|
2291
2250
|
int last_prune = m_chain.Height(); // last height we can prune
|
|
@@ -2293,24 +2252,6 @@ bool CChainState::FlushStateToDisk(
|
|
|
2293
2252
|
last_prune = std::max(1, std::min(last_prune, index.GetSummary().best_block_height));
|
|
2294
2253
|
});
|
|
2295
2254
|
|
|
2296
|
-
if (nManualPruneHeight > 0) {
|
|
2297
|
-
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCH);
|
|
2298
|
-
|
|
2299
|
-
m_blockman.FindFilesToPruneManual(setFilesToPrune, std::min(last_prune, nManualPruneHeight), m_chain.Height());
|
|
2300
|
-
} else {
|
|
2301
|
-
LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCH);
|
|
2302
|
-
|
|
2303
|
-
m_blockman.FindFilesToPrune(setFilesToPrune, m_params.PruneAfterHeight(), m_chain.Height(), last_prune, IsInitialBlockDownload());
|
|
2304
|
-
m_blockman.m_check_for_pruning = false;
|
|
2305
|
-
}
|
|
2306
|
-
if (!setFilesToPrune.empty()) {
|
|
2307
|
-
fFlushForPrune = true;
|
|
2308
|
-
if (!fHavePruned) {
|
|
2309
|
-
m_blockman.m_block_tree_db->WriteFlag("prunedblockfiles", true);
|
|
2310
|
-
fHavePruned = true;
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
2255
|
const auto nNow = GetTime<std::chrono::microseconds>();
|
|
2315
2256
|
// Avoid writing/flushing immediately after startup.
|
|
2316
2257
|
if (nLastWrite.count() == 0) {
|
|
@@ -2328,7 +2269,7 @@ bool CChainState::FlushStateToDisk(
|
|
|
2328
2269
|
// It's been very long since we flushed the cache. Do this infrequently, to optimize cache usage.
|
|
2329
2270
|
bool fPeriodicFlush = mode == FlushStateMode::PERIODIC && nNow > nLastFlush + DATABASE_FLUSH_INTERVAL;
|
|
2330
2271
|
// Combine all conditions that result in a full cache flush.
|
|
2331
|
-
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush
|
|
2272
|
+
fDoFullFlush = (mode == FlushStateMode::ALWAYS) || fCacheLarge || fCacheCritical || fPeriodicFlush;
|
|
2332
2273
|
// Write blocks and block index to disk.
|
|
2333
2274
|
if (fDoFullFlush || fPeriodicWrite) {
|
|
2334
2275
|
// Ensure we can write block index
|
|
@@ -2350,12 +2291,6 @@ bool CChainState::FlushStateToDisk(
|
|
|
2350
2291
|
return AbortNode(state, "Failed to write to block index database");
|
|
2351
2292
|
}
|
|
2352
2293
|
}
|
|
2353
|
-
// Finally remove any pruned files
|
|
2354
|
-
if (fFlushForPrune) {
|
|
2355
|
-
LOG_TIME_MILLIS_WITH_CATEGORY("unlink pruned files", BCLog::BENCH);
|
|
2356
|
-
|
|
2357
|
-
UnlinkPrunedFiles(setFilesToPrune);
|
|
2358
|
-
}
|
|
2359
2294
|
nLastWrite = nNow;
|
|
2360
2295
|
}
|
|
2361
2296
|
// Flush best chain related state. This can only be done if the blocks / block index write was also done.
|
|
@@ -2376,12 +2311,11 @@ bool CChainState::FlushStateToDisk(
|
|
|
2376
2311
|
return AbortNode(state, "Failed to write to coin database");
|
|
2377
2312
|
nLastFlush = nNow;
|
|
2378
2313
|
full_flush_completed = true;
|
|
2379
|
-
|
|
2314
|
+
TRACE4(utxocache, flush,
|
|
2380
2315
|
(int64_t)(GetTimeMicros() - nNow.count()), // in microseconds (µs)
|
|
2381
2316
|
(u_int32_t)mode,
|
|
2382
2317
|
(u_int64_t)coins_count,
|
|
2383
|
-
(u_int64_t)coins_mem_usage
|
|
2384
|
-
(bool)fFlushForPrune);
|
|
2318
|
+
(u_int64_t)coins_mem_usage);
|
|
2385
2319
|
}
|
|
2386
2320
|
}
|
|
2387
2321
|
if (full_flush_completed) {
|
|
@@ -2402,25 +2336,6 @@ void CChainState::ForceFlushStateToDisk()
|
|
|
2402
2336
|
}
|
|
2403
2337
|
}
|
|
2404
2338
|
|
|
2405
|
-
void CChainState::PruneAndFlush()
|
|
2406
|
-
{
|
|
2407
|
-
BlockValidationState state;
|
|
2408
|
-
m_blockman.m_check_for_pruning = true;
|
|
2409
|
-
if (!this->FlushStateToDisk(state, FlushStateMode::NONE)) {
|
|
2410
|
-
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
|
|
2411
|
-
}
|
|
2412
|
-
}
|
|
2413
|
-
|
|
2414
|
-
static void DoWarning(const bilingual_str& warning)
|
|
2415
|
-
{
|
|
2416
|
-
static bool fWarned = false;
|
|
2417
|
-
SetMiscWarning(warning);
|
|
2418
|
-
if (!fWarned) {
|
|
2419
|
-
AlertNotify(warning.original);
|
|
2420
|
-
fWarned = true;
|
|
2421
|
-
}
|
|
2422
|
-
}
|
|
2423
|
-
|
|
2424
2339
|
/** Private helper function that concatenates warning messages. */
|
|
2425
2340
|
static void AppendWarning(bilingual_str& res, const bilingual_str& warn)
|
|
2426
2341
|
{
|
|
@@ -2441,7 +2356,7 @@ static void UpdateTipLog(
|
|
|
2441
2356
|
LogPrintf("%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo)%s\n",
|
|
2442
2357
|
prefix, func_name,
|
|
2443
2358
|
tip->GetBlockHash().ToString(), tip->nHeight, tip->nVersion,
|
|
2444
|
-
log(tip->
|
|
2359
|
+
log(tip->nChainTrust.getdouble()) / log(2.0), (unsigned long)tip->nChainTx,
|
|
2445
2360
|
FormatISO8601DateTime(tip->GetBlockTime()),
|
|
2446
2361
|
GuessVerificationProgress(params.TxData(), tip),
|
|
2447
2362
|
coins_tip.DynamicMemoryUsage() * (1.0 / (1 << 20)),
|
|
@@ -2479,19 +2394,8 @@ void CChainState::UpdateTip(const CBlockIndex* pindexNew)
|
|
|
2479
2394
|
bilingual_str warning_messages;
|
|
2480
2395
|
if (!this->IsInitialBlockDownload()) {
|
|
2481
2396
|
const CBlockIndex* pindex = pindexNew;
|
|
2482
|
-
for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) {
|
|
2483
|
-
WarningBitsConditionChecker checker(bit);
|
|
2484
|
-
ThresholdState state = checker.GetStateFor(pindex, m_params.GetConsensus(), warningcache[bit]);
|
|
2485
|
-
if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) {
|
|
2486
|
-
const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit);
|
|
2487
|
-
if (state == ThresholdState::ACTIVE) {
|
|
2488
|
-
DoWarning(warning);
|
|
2489
|
-
} else {
|
|
2490
|
-
AppendWarning(warning_messages, warning);
|
|
2491
|
-
}
|
|
2492
|
-
}
|
|
2493
|
-
}
|
|
2494
2397
|
}
|
|
2398
|
+
|
|
2495
2399
|
UpdateTipLog(coins_tip, pindexNew, m_params, __func__, "", warning_messages.original);
|
|
2496
2400
|
}
|
|
2497
2401
|
|
|
@@ -2704,7 +2608,7 @@ CBlockIndex* CChainState::FindMostWorkChain()
|
|
|
2704
2608
|
bool fMissingData = !(pindexTest->nStatus & BLOCK_HAVE_DATA);
|
|
2705
2609
|
if (fFailedChain || fMissingData) {
|
|
2706
2610
|
// Candidate chain is not usable (either invalid or missing data)
|
|
2707
|
-
if (fFailedChain && (m_chainman.m_best_invalid == nullptr || pindexNew->
|
|
2611
|
+
if (fFailedChain && (m_chainman.m_best_invalid == nullptr || pindexNew->nChainTrust > m_chainman.m_best_invalid->nChainTrust)) {
|
|
2708
2612
|
m_chainman.m_best_invalid = pindexNew;
|
|
2709
2613
|
}
|
|
2710
2614
|
CBlockIndex *pindexFailed = pindexNew;
|
|
@@ -2815,7 +2719,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, CBlockIndex
|
|
|
2815
2719
|
}
|
|
2816
2720
|
} else {
|
|
2817
2721
|
PruneBlockIndexCandidates();
|
|
2818
|
-
if (!pindexOldTip || m_chain.Tip()->
|
|
2722
|
+
if (!pindexOldTip || m_chain.Tip()->nChainTrust > pindexOldTip->nChainTrust) {
|
|
2819
2723
|
// We're in a better position than we were. Return temporarily to release the lock.
|
|
2820
2724
|
fContinue = false;
|
|
2821
2725
|
break;
|
|
@@ -2981,15 +2885,15 @@ bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex
|
|
|
2981
2885
|
AssertLockNotHeld(::cs_main);
|
|
2982
2886
|
{
|
|
2983
2887
|
LOCK(cs_main);
|
|
2984
|
-
if (pindex->
|
|
2888
|
+
if (pindex->nChainTrust < m_chain.Tip()->nChainTrust) {
|
|
2985
2889
|
// Nothing to do, this block is not at the tip.
|
|
2986
2890
|
return true;
|
|
2987
2891
|
}
|
|
2988
|
-
if (m_chain.Tip()->
|
|
2892
|
+
if (m_chain.Tip()->nChainTrust > nLastPreciousChainwork) {
|
|
2989
2893
|
// The chain has been extended since the last call, reset the counter.
|
|
2990
2894
|
nBlockReverseSequenceId = -1;
|
|
2991
2895
|
}
|
|
2992
|
-
nLastPreciousChainwork = m_chain.Tip()->
|
|
2896
|
+
nLastPreciousChainwork = m_chain.Tip()->nChainTrust;
|
|
2993
2897
|
setBlockIndexCandidates.erase(pindex);
|
|
2994
2898
|
pindex->nSequenceId = nBlockReverseSequenceId;
|
|
2995
2899
|
if (nBlockReverseSequenceId > std::numeric_limits<int32_t>::min()) {
|
|
@@ -3046,7 +2950,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
|
|
|
3046
2950
|
!CBlockIndexWorkComparator()(candidate, pindex->pprev) &&
|
|
3047
2951
|
candidate->IsValid(BLOCK_VALID_TRANSACTIONS) &&
|
|
3048
2952
|
candidate->HaveTxsDownloaded()) {
|
|
3049
|
-
candidate_blocks_by_work.insert(std::make_pair(candidate->
|
|
2953
|
+
candidate_blocks_by_work.insert(std::make_pair(candidate->nChainTrust, candidate));
|
|
3050
2954
|
}
|
|
3051
2955
|
}
|
|
3052
2956
|
}
|
|
@@ -3096,7 +3000,7 @@ bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pind
|
|
|
3096
3000
|
}
|
|
3097
3001
|
|
|
3098
3002
|
// Add any equal or more work headers to setBlockIndexCandidates
|
|
3099
|
-
auto candidate_it = candidate_blocks_by_work.lower_bound(invalid_walk_tip->pprev->
|
|
3003
|
+
auto candidate_it = candidate_blocks_by_work.lower_bound(invalid_walk_tip->pprev->nChainTrust);
|
|
3100
3004
|
while (candidate_it != candidate_blocks_by_work.end()) {
|
|
3101
3005
|
if (!CBlockIndexWorkComparator()(candidate_it->second, invalid_walk_tip->pprev)) {
|
|
3102
3006
|
setBlockIndexCandidates.insert(candidate_it->second);
|
|
@@ -3195,7 +3099,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi
|
|
|
3195
3099
|
pindexNew->nDataPos = pos.nPos;
|
|
3196
3100
|
pindexNew->nUndoPos = 0;
|
|
3197
3101
|
pindexNew->nStatus |= BLOCK_HAVE_DATA;
|
|
3198
|
-
if (
|
|
3102
|
+
if (pindexNew->pprev && IsBTC16BIPsEnabled(pindexNew->pprev->nTime)) {
|
|
3199
3103
|
pindexNew->nStatus |= BLOCK_OPT_WITNESS;
|
|
3200
3104
|
}
|
|
3201
3105
|
pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS);
|
|
@@ -3239,7 +3143,7 @@ static bool CheckBlockHeader(const CBlockHeader& block, BlockValidationState& st
|
|
|
3239
3143
|
return true;
|
|
3240
3144
|
}
|
|
3241
3145
|
|
|
3242
|
-
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot)
|
|
3146
|
+
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSignature)
|
|
3243
3147
|
{
|
|
3244
3148
|
// These are checks that are independent of context.
|
|
3245
3149
|
|
|
@@ -3248,7 +3152,7 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
|
|
|
3248
3152
|
|
|
3249
3153
|
// Check that the header is valid (particularly PoW). This is mostly
|
|
3250
3154
|
// redundant with the call in AcceptBlockHeader.
|
|
3251
|
-
if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW))
|
|
3155
|
+
if (!CheckBlockHeader(block, state, consensusParams, fCheckPOW && !block.IsProofOfStake()))
|
|
3252
3156
|
return false;
|
|
3253
3157
|
|
|
3254
3158
|
// Signet only: check block solution
|
|
@@ -3287,6 +3191,32 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
|
|
|
3287
3191
|
if (block.vtx[i]->IsCoinBase())
|
|
3288
3192
|
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-multiple", "more than one coinbase");
|
|
3289
3193
|
|
|
3194
|
+
// peercoin: only the second transaction can be the optional coinstake
|
|
3195
|
+
for (unsigned int i = 2; i < block.vtx.size(); i++)
|
|
3196
|
+
if (block.vtx[i]->IsCoinStake())
|
|
3197
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cs-missing", "coinstake in wrong position");
|
|
3198
|
+
|
|
3199
|
+
// peercoin: first coinbase output should be empty if proof-of-stake block
|
|
3200
|
+
if (block.IsProofOfStake() && !block.vtx[0]->vout[0].IsEmpty())
|
|
3201
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-notempty", "coinbase output not empty in PoS block");
|
|
3202
|
+
|
|
3203
|
+
// Check coinbase timestamp
|
|
3204
|
+
if (block.GetBlockTime() > (block.vtx[0]->nTime ? (int64_t)block.vtx[0]->nTime : block.GetBlockTime()) + (IsProtocolV09(block.GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9))
|
|
3205
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-time", "coinbase timestamp is too early");
|
|
3206
|
+
|
|
3207
|
+
// Check coinstake timestamp
|
|
3208
|
+
if (block.IsProofOfStake() && !CheckCoinStakeTimestamp(block.GetBlockTime(), block.vtx[1]->nTime ? (int64_t)block.vtx[1]->nTime : block.GetBlockTime()))
|
|
3209
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cs-time", "coinstake timestamp violation");
|
|
3210
|
+
|
|
3211
|
+
// Check coinbase reward
|
|
3212
|
+
CAmount nCoinbaseCost = 0;
|
|
3213
|
+
if (block.IsProofOfWork())
|
|
3214
|
+
nCoinbaseCost = (GetMinFee(*block.vtx[0], block.nTime) < PERKB_TX_FEE)? 0 : (GetMinFee(*block.vtx[0], block.nTime) - PERKB_TX_FEE);
|
|
3215
|
+
if (block.vtx[0]->GetValueOut() > (block.IsProofOfWork()? (GetProofOfWorkReward(block.nBits, block.GetBlockTime()) - nCoinbaseCost) : 0))
|
|
3216
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cb-amount",
|
|
3217
|
+
strprintf("CheckBlock() : coinbase reward exceeded %s > %s",
|
|
3218
|
+
FormatMoney(block.vtx[0]->GetValueOut()),
|
|
3219
|
+
FormatMoney(block.IsProofOfWork()? GetProofOfWorkReward(block.nBits, block.GetBlockTime()) : 0)));
|
|
3290
3220
|
// Check transactions
|
|
3291
3221
|
// Must check for duplicate inputs (see CVE-2018-17144)
|
|
3292
3222
|
for (const auto& tx : block.vtx) {
|
|
@@ -3297,6 +3227,9 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
|
|
|
3297
3227
|
assert(tx_state.GetResult() == TxValidationResult::TX_CONSENSUS);
|
|
3298
3228
|
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, tx_state.GetRejectReason(),
|
|
3299
3229
|
strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), tx_state.GetDebugMessage()));
|
|
3230
|
+
// peercoin: check transaction timestamp
|
|
3231
|
+
if (block.GetBlockTime() < (int64_t)tx->nTime)
|
|
3232
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-tx-time", strprintf("%s : block timestamp earlier than transaction timestamp", __func__));
|
|
3300
3233
|
}
|
|
3301
3234
|
}
|
|
3302
3235
|
unsigned int nSigOps = 0;
|
|
@@ -3310,6 +3243,12 @@ bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensu
|
|
|
3310
3243
|
if (fCheckPOW && fCheckMerkleRoot)
|
|
3311
3244
|
block.fChecked = true;
|
|
3312
3245
|
|
|
3246
|
+
// peercoin: check block signature
|
|
3247
|
+
// Only check block signature if check merkle root, c.f. commit 3cd01fdf
|
|
3248
|
+
// rfc6: validate signatures of proof of stake blocks only after 0.8 fork
|
|
3249
|
+
if (fCheckMerkleRoot && fCheckSignature && (block.IsProofOfStake() || !IsBTC16BIPsEnabled(block.GetBlockTime())) && !CheckBlockSignature(block))
|
|
3250
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-blk-sign", strprintf("%s : bad block signature", __func__));
|
|
3251
|
+
|
|
3313
3252
|
return true;
|
|
3314
3253
|
}
|
|
3315
3254
|
|
|
@@ -3317,7 +3256,7 @@ void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPr
|
|
|
3317
3256
|
{
|
|
3318
3257
|
int commitpos = GetWitnessCommitmentIndex(block);
|
|
3319
3258
|
static const std::vector<unsigned char> nonce(32, 0x00);
|
|
3320
|
-
if (commitpos != NO_WITNESS_COMMITMENT &&
|
|
3259
|
+
if (commitpos != NO_WITNESS_COMMITMENT && IsBTC16BIPsEnabled(pindexPrev->nTime) && !block.vtx[0]->HasWitness()) {
|
|
3321
3260
|
CMutableTransaction tx(*block.vtx[0]);
|
|
3322
3261
|
tx.vin[0].scriptWitness.stack.resize(1);
|
|
3323
3262
|
tx.vin[0].scriptWitness.stack[0] = nonce;
|
|
@@ -3367,10 +3306,10 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
|
|
3367
3306
|
assert(pindexPrev != nullptr);
|
|
3368
3307
|
const int nHeight = pindexPrev->nHeight + 1;
|
|
3369
3308
|
|
|
3370
|
-
// Check proof of work
|
|
3309
|
+
// Check proof of work or proof-of-stake
|
|
3371
3310
|
const Consensus::Params& consensusParams = params.GetConsensus();
|
|
3372
|
-
if (block.nBits !=
|
|
3373
|
-
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", "incorrect proof of work");
|
|
3311
|
+
if (block.nBits != GetNextTargetRequired(pindexPrev, block.nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE, consensusParams))
|
|
3312
|
+
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-diffbits", "incorrect proof of work/stake");
|
|
3374
3313
|
|
|
3375
3314
|
// Check against checkpoints
|
|
3376
3315
|
if (fCheckpointsEnabled) {
|
|
@@ -3385,20 +3324,19 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
|
|
3385
3324
|
}
|
|
3386
3325
|
|
|
3387
3326
|
// Check timestamp against prev
|
|
3388
|
-
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast())
|
|
3327
|
+
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast() || block.GetBlockTime() + (IsProtocolV09(block.GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9) < pindexPrev->GetBlockTime())
|
|
3389
3328
|
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-too-old", "block's timestamp is too early");
|
|
3390
3329
|
|
|
3391
3330
|
// Check timestamp
|
|
3392
|
-
if (block.GetBlockTime() > nAdjustedTime + MAX_FUTURE_BLOCK_TIME)
|
|
3331
|
+
if (block.GetBlockTime() > nAdjustedTime + (IsProtocolV09(block.GetBlockTime()) ? MAX_FUTURE_BLOCK_TIME : MAX_FUTURE_BLOCK_TIME_PREV9))
|
|
3393
3332
|
return state.Invalid(BlockValidationResult::BLOCK_TIME_FUTURE, "time-too-new", "block timestamp too far in the future");
|
|
3394
3333
|
|
|
3395
|
-
// Reject blocks
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
(block.nVersion < 4 &&
|
|
3334
|
+
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
|
|
3335
|
+
// check for version 2, 3 and 4 upgrades
|
|
3336
|
+
if ((block.nVersion < 2 && IsProtocolV06(pindexPrev)) ||
|
|
3337
|
+
(block.nVersion < 4 && IsProtocolV12(pindexPrev)))
|
|
3399
3338
|
return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, strprintf("bad-version(0x%08x)", block.nVersion),
|
|
3400
3339
|
strprintf("rejected nVersion=0x%08x block", block.nVersion));
|
|
3401
|
-
}
|
|
3402
3340
|
|
|
3403
3341
|
return true;
|
|
3404
3342
|
}
|
|
@@ -3409,13 +3347,13 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio
|
|
|
3409
3347
|
* in ConnectBlock().
|
|
3410
3348
|
* Note that -reindex-chainstate skips the validation that happens here!
|
|
3411
3349
|
*/
|
|
3412
|
-
static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const
|
|
3350
|
+
static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& state, const CBlockIndex* pindexPrev)
|
|
3413
3351
|
{
|
|
3414
3352
|
const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1;
|
|
3415
3353
|
|
|
3416
3354
|
// Enforce BIP113 (Median Time Past).
|
|
3417
3355
|
int nLockTimeFlags = 0;
|
|
3418
|
-
if (
|
|
3356
|
+
if (pindexPrev && IsBTC16BIPsEnabled(pindexPrev->nTime)) {
|
|
3419
3357
|
assert(pindexPrev != nullptr);
|
|
3420
3358
|
nLockTimeFlags |= LOCKTIME_MEDIAN_TIME_PAST;
|
|
3421
3359
|
}
|
|
@@ -3432,7 +3370,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
|
|
|
3432
3370
|
}
|
|
3433
3371
|
|
|
3434
3372
|
// Enforce rule that the coinbase starts with serialized block height
|
|
3435
|
-
if (
|
|
3373
|
+
if (pindexPrev && IsProtocolV06(pindexPrev) && block.nVersion >= 2)
|
|
3436
3374
|
{
|
|
3437
3375
|
CScript expect = CScript() << nHeight;
|
|
3438
3376
|
if (block.vtx[0]->vin[0].scriptSig.size() < expect.size() ||
|
|
@@ -3450,7 +3388,7 @@ static bool ContextualCheckBlock(const CBlock& block, BlockValidationState& stat
|
|
|
3450
3388
|
// {0xaa, 0x21, 0xa9, 0xed}, and the following 32 bytes are SHA256^2(witness root, witness reserved value). In case there are
|
|
3451
3389
|
// multiple, the last one is used.
|
|
3452
3390
|
bool fHaveWitness = false;
|
|
3453
|
-
if (
|
|
3391
|
+
if (pindexPrev && IsBTC16BIPsEnabled(pindexPrev->nTime)) {
|
|
3454
3392
|
int commitpos = GetWitnessCommitmentIndex(block);
|
|
3455
3393
|
if (commitpos != NO_WITNESS_COMMITMENT) {
|
|
3456
3394
|
bool malleated = false;
|
|
@@ -3510,7 +3448,7 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
|
|
|
3510
3448
|
return true;
|
|
3511
3449
|
}
|
|
3512
3450
|
|
|
3513
|
-
if (!CheckBlockHeader(block, state, chainparams.GetConsensus())) {
|
|
3451
|
+
if (!CheckBlockHeader(block, state, chainparams.GetConsensus(), !(block.nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE))) {
|
|
3514
3452
|
LogPrint(BCLog::VALIDATION, "%s: Consensus::CheckBlockHeader: %s, %s\n", __func__, hash.ToString(), state.ToString());
|
|
3515
3453
|
return false;
|
|
3516
3454
|
}
|
|
@@ -3580,22 +3518,39 @@ bool ChainstateManager::AcceptBlockHeader(const CBlockHeader& block, BlockValida
|
|
|
3580
3518
|
}
|
|
3581
3519
|
|
|
3582
3520
|
// Exposed wrapper for AcceptBlockHeader
|
|
3583
|
-
bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
|
|
3521
|
+
bool ChainstateManager::ProcessNewBlockHeaders(int32_t& nPoSTemperature, const uint256& lastAcceptedHeader, const std::vector<CBlockHeader>& headers, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex)
|
|
3584
3522
|
{
|
|
3585
3523
|
AssertLockNotHeld(cs_main);
|
|
3586
3524
|
{
|
|
3587
3525
|
LOCK(cs_main);
|
|
3526
|
+
|
|
3527
|
+
int nCooling = POW_HEADER_COOLING;
|
|
3528
|
+
if (headers[0].hashPrevBlock != lastAcceptedHeader && !lastAcceptedHeader.IsNull()) {
|
|
3529
|
+
nPoSTemperature += (18 + headers.size()) / 10;
|
|
3530
|
+
nCooling = 0;
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3588
3533
|
for (const CBlockHeader& header : headers) {
|
|
3534
|
+
bool fPoS = header.nFlags & CBlockIndex::BLOCK_PROOF_OF_STAKE;
|
|
3535
|
+
|
|
3589
3536
|
CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast
|
|
3590
3537
|
bool accepted{AcceptBlockHeader(header, state, chainparams, &pindex)};
|
|
3591
3538
|
ActiveChainstate().CheckBlockIndex();
|
|
3592
3539
|
|
|
3593
3540
|
if (!accepted) {
|
|
3541
|
+
nPoSTemperature += POW_HEADER_COOLING;
|
|
3594
3542
|
return false;
|
|
3595
3543
|
}
|
|
3596
3544
|
if (ppindex) {
|
|
3597
3545
|
*ppindex = pindex;
|
|
3598
3546
|
}
|
|
3547
|
+
|
|
3548
|
+
if (fPoS) {
|
|
3549
|
+
nPoSTemperature++;
|
|
3550
|
+
} else { // PoW
|
|
3551
|
+
nPoSTemperature -= nCooling;
|
|
3552
|
+
nPoSTemperature = std::max((int)nPoSTemperature, 0);
|
|
3553
|
+
}
|
|
3599
3554
|
}
|
|
3600
3555
|
}
|
|
3601
3556
|
if (NotifyHeaderTip(ActiveChainstate())) {
|
|
@@ -3613,6 +3568,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector<CBlockHeader>&
|
|
|
3613
3568
|
bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, BlockValidationState& state, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock)
|
|
3614
3569
|
{
|
|
3615
3570
|
const CBlock& block = *pblock;
|
|
3571
|
+
bool fCheckPoS = true;
|
|
3616
3572
|
|
|
3617
3573
|
if (fNewBlock) *fNewBlock = false;
|
|
3618
3574
|
AssertLockHeld(cs_main);
|
|
@@ -3626,11 +3582,17 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|
|
3626
3582
|
if (!accepted_header)
|
|
3627
3583
|
return false;
|
|
3628
3584
|
|
|
3585
|
+
// peercoin: we should only accept blocks that can be connected to a prev block with validated PoS
|
|
3586
|
+
if (fCheckPoS && pindex->pprev && !pindex->pprev->IsValid(BLOCK_VALID_TRANSACTIONS)) {
|
|
3587
|
+
return error("%s: this block does not connect to any valid known block", __func__);
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3629
3590
|
// Try to process all requested blocks that we don't have, but only
|
|
3630
3591
|
// process an unrequested block if it's new and has enough work to
|
|
3631
3592
|
// advance our tip, and isn't too many blocks ahead.
|
|
3632
3593
|
bool fAlreadyHave = pindex->nStatus & BLOCK_HAVE_DATA;
|
|
3633
|
-
bool fHasMoreOrSameWork = (m_chain.Tip() ? pindex->
|
|
3594
|
+
bool fHasMoreOrSameWork = (m_chain.Tip() ? pindex->nChainTrust >= m_chain.Tip()->nChainTrust : true);
|
|
3595
|
+
|
|
3634
3596
|
// Blocks that are too out-of-order needlessly limit the effectiveness of
|
|
3635
3597
|
// pruning, because pruning will not delete block files that contain any
|
|
3636
3598
|
// blocks which are too close in height to the tip. Apply this test
|
|
@@ -3656,11 +3618,11 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|
|
3656
3618
|
// If our tip is behind, a peer could try to send us
|
|
3657
3619
|
// low-work blocks on a fake chain that we would never
|
|
3658
3620
|
// request; don't process these.
|
|
3659
|
-
if (pindex->
|
|
3621
|
+
if (pindex->nChainTrust < nMinimumChainWork) return true;
|
|
3660
3622
|
}
|
|
3661
3623
|
|
|
3662
3624
|
if (!CheckBlock(block, state, m_params.GetConsensus()) ||
|
|
3663
|
-
!ContextualCheckBlock(block, state,
|
|
3625
|
+
!ContextualCheckBlock(block, state, pindex->pprev)) {
|
|
3664
3626
|
if (state.IsInvalid() && state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
|
|
3665
3627
|
pindex->nStatus |= BLOCK_FAILED_VALID;
|
|
3666
3628
|
m_blockman.m_dirty_blockindex.insert(pindex);
|
|
@@ -3668,9 +3630,16 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|
|
3668
3630
|
return error("%s: %s", __func__, state.ToString());
|
|
3669
3631
|
}
|
|
3670
3632
|
|
|
3633
|
+
// peercoin: check PoS
|
|
3634
|
+
if (fCheckPoS && !PeercoinContextualBlockChecks(block, state, pindex, false, m_chainman.ActiveChainstate())) {
|
|
3635
|
+
pindex->nStatus |= BLOCK_FAILED_VALID;
|
|
3636
|
+
m_blockman.m_dirty_blockindex.insert(pindex);
|
|
3637
|
+
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-pos", "proof of stake is incorrect");
|
|
3638
|
+
}
|
|
3639
|
+
|
|
3671
3640
|
// Header is valid/has work, merkle tree and segwit merkle tree are good...RELAY NOW
|
|
3672
3641
|
// (but if it does not build on our best tip, let the SendMessages loop relay it)
|
|
3673
|
-
if (!IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev)
|
|
3642
|
+
if (!m_chainman.ActiveChainstate().IsInitialBlockDownload() && m_chain.Tip() == pindex->pprev)
|
|
3674
3643
|
GetMainSignals().NewPoWValidBlock(pindex, pblock);
|
|
3675
3644
|
|
|
3676
3645
|
// Write block to history file
|
|
@@ -3687,19 +3656,19 @@ bool CChainState::AcceptBlock(const std::shared_ptr<const CBlock>& pblock, Block
|
|
|
3687
3656
|
}
|
|
3688
3657
|
|
|
3689
3658
|
FlushStateToDisk(state, FlushStateMode::NONE);
|
|
3690
|
-
|
|
3691
3659
|
CheckBlockIndex();
|
|
3692
3660
|
|
|
3693
3661
|
return true;
|
|
3694
3662
|
}
|
|
3695
3663
|
|
|
3696
|
-
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block)
|
|
3664
|
+
bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block, CBlockIndex** ppindex, bool* fPoSDuplicate)
|
|
3697
3665
|
{
|
|
3698
3666
|
AssertLockNotHeld(cs_main);
|
|
3699
3667
|
|
|
3700
3668
|
{
|
|
3701
3669
|
CBlockIndex *pindex = nullptr;
|
|
3702
3670
|
if (new_block) *new_block = false;
|
|
3671
|
+
if (fPoSDuplicate) *fPoSDuplicate = false;
|
|
3703
3672
|
BlockValidationState state;
|
|
3704
3673
|
|
|
3705
3674
|
// CheckBlock() does not support multi-threaded block validation because CBlock::fChecked can cause data race.
|
|
@@ -3716,10 +3685,19 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s
|
|
|
3716
3685
|
// Store to disk
|
|
3717
3686
|
ret = ActiveChainstate().AcceptBlock(block, state, &pindex, force_processing, nullptr, new_block);
|
|
3718
3687
|
}
|
|
3688
|
+
if (ppindex)
|
|
3689
|
+
*ppindex = ret ? pindex : nullptr;
|
|
3719
3690
|
if (!ret) {
|
|
3720
3691
|
GetMainSignals().BlockChecked(*block, state);
|
|
3721
3692
|
return error("%s: AcceptBlock FAILED (%s)", __func__, state.ToString());
|
|
3722
3693
|
}
|
|
3694
|
+
|
|
3695
|
+
if (pindex->IsProofOfStake() && !ActiveChainstate().IsInitialBlockDownload()) {
|
|
3696
|
+
int32_t ndx = univHash(pindex->hashProofOfStake);
|
|
3697
|
+
if (fPoSDuplicate && vStakeSeen[ndx] == pindex->hashProofOfStake)
|
|
3698
|
+
*fPoSDuplicate = true;
|
|
3699
|
+
vStakeSeen[ndx] = pindex->hashProofOfStake;
|
|
3700
|
+
}
|
|
3723
3701
|
}
|
|
3724
3702
|
|
|
3725
3703
|
NotifyHeaderTip(ActiveChainstate());
|
|
@@ -3768,7 +3746,7 @@ bool TestBlockValidity(BlockValidationState& state,
|
|
|
3768
3746
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, state.ToString());
|
|
3769
3747
|
if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot))
|
|
3770
3748
|
return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString());
|
|
3771
|
-
if (!ContextualCheckBlock(block, state,
|
|
3749
|
+
if (!ContextualCheckBlock(block, state, pindexPrev))
|
|
3772
3750
|
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, state.ToString());
|
|
3773
3751
|
if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, true)) {
|
|
3774
3752
|
return false;
|
|
@@ -3778,16 +3756,6 @@ bool TestBlockValidity(BlockValidationState& state,
|
|
|
3778
3756
|
return true;
|
|
3779
3757
|
}
|
|
3780
3758
|
|
|
3781
|
-
/* This function is called from the RPC code for pruneblockchain */
|
|
3782
|
-
void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight)
|
|
3783
|
-
{
|
|
3784
|
-
BlockValidationState state;
|
|
3785
|
-
if (!active_chainstate.FlushStateToDisk(
|
|
3786
|
-
state, FlushStateMode::NONE, nManualPruneHeight)) {
|
|
3787
|
-
LogPrintf("%s: failed to flush state (%s)\n", __func__, state.ToString());
|
|
3788
|
-
}
|
|
3789
|
-
}
|
|
3790
|
-
|
|
3791
3759
|
void CChainState::LoadMempool(const ArgsManager& args)
|
|
3792
3760
|
{
|
|
3793
3761
|
if (!m_mempool) return;
|
|
@@ -3874,10 +3842,10 @@ bool CVerifyDB::VerifyDB(
|
|
|
3874
3842
|
if (pindex->nHeight <= chainstate.m_chain.Height() - nCheckDepth) {
|
|
3875
3843
|
break;
|
|
3876
3844
|
}
|
|
3877
|
-
if (
|
|
3878
|
-
// If
|
|
3845
|
+
if (is_snapshot_cs && !(pindex->nStatus & BLOCK_HAVE_DATA)) {
|
|
3846
|
+
// If running under an assumeutxo snapshot, only go
|
|
3879
3847
|
// back as far as we have data.
|
|
3880
|
-
LogPrintf("VerifyDB(): block verification stopping at height %d (
|
|
3848
|
+
LogPrintf("VerifyDB(): block verification stopping at height %d (no data)\n", pindex->nHeight);
|
|
3881
3849
|
break;
|
|
3882
3850
|
}
|
|
3883
3851
|
CBlock block;
|
|
@@ -3968,7 +3936,7 @@ bool CChainState::RollforwardBlock(const CBlockIndex* pindex, CCoinsViewCache& i
|
|
|
3968
3936
|
}
|
|
3969
3937
|
}
|
|
3970
3938
|
// Pass check = true as every addition may be an overwrite.
|
|
3971
|
-
AddCoins(inputs, *tx, pindex->nHeight, true);
|
|
3939
|
+
AddCoins(inputs, *tx, pindex->nHeight, true, IsProtocolV12(pindex));
|
|
3972
3940
|
}
|
|
3973
3941
|
return true;
|
|
3974
3942
|
}
|
|
@@ -4047,7 +4015,7 @@ bool CChainState::NeedsRedownload() const
|
|
|
4047
4015
|
// At and above m_params.SegwitHeight, segwit consensus rules must be validated
|
|
4048
4016
|
CBlockIndex* block{m_chain.Tip()};
|
|
4049
4017
|
|
|
4050
|
-
while (block != nullptr &&
|
|
4018
|
+
while (block != nullptr && block->pprev && IsBTC16BIPsEnabled(block->pprev->nTime)) {
|
|
4051
4019
|
if (!(block->nStatus & BLOCK_OPT_WITNESS)) {
|
|
4052
4020
|
// block is insufficiently validated for a segwit client
|
|
4053
4021
|
return true;
|
|
@@ -4074,11 +4042,6 @@ void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman)
|
|
|
4074
4042
|
chainman.Unload();
|
|
4075
4043
|
pindexBestHeader = nullptr;
|
|
4076
4044
|
if (mempool) mempool->clear();
|
|
4077
|
-
g_versionbitscache.Clear();
|
|
4078
|
-
for (int b = 0; b < VERSIONBITS_NUM_BITS; b++) {
|
|
4079
|
-
warningcache[b].clear();
|
|
4080
|
-
}
|
|
4081
|
-
fHavePruned = false;
|
|
4082
4045
|
}
|
|
4083
4046
|
|
|
4084
4047
|
bool ChainstateManager::LoadBlockIndex()
|
|
@@ -4330,16 +4293,8 @@ void CChainState::CheckBlockIndex()
|
|
|
4330
4293
|
if (!pindex->HaveTxsDownloaded()) assert(pindex->nSequenceId <= 0); // nSequenceId can't be set positive for blocks that aren't linked (negative is used for preciousblock)
|
|
4331
4294
|
// VALID_TRANSACTIONS is equivalent to nTx > 0 for all nodes (whether or not pruning has occurred).
|
|
4332
4295
|
// HAVE_DATA is only equivalent to nTx > 0 (or VALID_TRANSACTIONS) if no pruning has occurred.
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
if (!fHavePruned && !pindex->IsAssumedValid()) {
|
|
4336
|
-
// If we've never pruned, then HAVE_DATA should be equivalent to nTx > 0
|
|
4337
|
-
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
|
|
4338
|
-
assert(pindexFirstMissing == pindexFirstNeverProcessed);
|
|
4339
|
-
} else {
|
|
4340
|
-
// If we have pruned, then we can only say that HAVE_DATA implies nTx > 0
|
|
4341
|
-
if (pindex->nStatus & BLOCK_HAVE_DATA) assert(pindex->nTx > 0);
|
|
4342
|
-
}
|
|
4296
|
+
assert(!(pindex->nStatus & BLOCK_HAVE_DATA) == (pindex->nTx == 0));
|
|
4297
|
+
assert(pindexFirstMissing == pindexFirstNeverProcessed);
|
|
4343
4298
|
if (pindex->nStatus & BLOCK_HAVE_UNDO) assert(pindex->nStatus & BLOCK_HAVE_DATA);
|
|
4344
4299
|
if (pindex->IsAssumedValid()) {
|
|
4345
4300
|
// Assumed-valid blocks should have some nTx value.
|
|
@@ -4355,7 +4310,7 @@ void CChainState::CheckBlockIndex()
|
|
|
4355
4310
|
assert((pindexFirstNeverProcessed == nullptr) == pindex->HaveTxsDownloaded());
|
|
4356
4311
|
assert((pindexFirstNotTransactionsValid == nullptr) == pindex->HaveTxsDownloaded());
|
|
4357
4312
|
assert(pindex->nHeight == nHeight); // nHeight must be consistent.
|
|
4358
|
-
assert(pindex->pprev == nullptr || pindex->
|
|
4313
|
+
assert(pindex->pprev == nullptr || pindex->nChainTrust >= pindex->pprev->nChainTrust); // For every block except the genesis block, the chainwork must be larger than the parent's.
|
|
4359
4314
|
assert(nHeight < 2 || (pindex->pskip && (pindex->pskip->nHeight < nHeight))); // The pskip pointer must point back for all but the first 2 blocks.
|
|
4360
4315
|
assert(pindexFirstNotTreeValid == nullptr); // All m_blockman.m_block_index entries must at least be TREE valid
|
|
4361
4316
|
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TREE) assert(pindexFirstNotTreeValid == nullptr); // TREE valid implies all parents are TREE valid
|
|
@@ -4371,8 +4326,7 @@ void CChainState::CheckBlockIndex()
|
|
|
4371
4326
|
|
|
4372
4327
|
// If this block sorts at least as good as the current tip and
|
|
4373
4328
|
// is valid and we have all data for its parents, it must be in
|
|
4374
|
-
// setBlockIndexCandidates.
|
|
4375
|
-
// even if some data has been pruned.
|
|
4329
|
+
// setBlockIndexCandidates. m_chain.Tip() must also be there.
|
|
4376
4330
|
//
|
|
4377
4331
|
// Don't perform this check for the background chainstate since
|
|
4378
4332
|
// its setBlockIndexCandidates shouldn't have some entries (i.e. those past the
|
|
@@ -4404,23 +4358,6 @@ void CChainState::CheckBlockIndex()
|
|
|
4404
4358
|
}
|
|
4405
4359
|
if (!(pindex->nStatus & BLOCK_HAVE_DATA)) assert(!foundInUnlinked); // Can't be in m_blocks_unlinked if we don't HAVE_DATA
|
|
4406
4360
|
if (pindexFirstMissing == nullptr) assert(!foundInUnlinked); // We aren't missing data for any parent -- cannot be in m_blocks_unlinked.
|
|
4407
|
-
if (pindex->pprev && (pindex->nStatus & BLOCK_HAVE_DATA) && pindexFirstNeverProcessed == nullptr && pindexFirstMissing != nullptr) {
|
|
4408
|
-
// We HAVE_DATA for this block, have received data for all parents at some point, but we're currently missing data for some parent.
|
|
4409
|
-
assert(fHavePruned); // We must have pruned.
|
|
4410
|
-
// This block may have entered m_blocks_unlinked if:
|
|
4411
|
-
// - it has a descendant that at some point had more work than the
|
|
4412
|
-
// tip, and
|
|
4413
|
-
// - we tried switching to that descendant but were missing
|
|
4414
|
-
// data for some intermediate block between m_chain and the
|
|
4415
|
-
// tip.
|
|
4416
|
-
// So if this block is itself better than m_chain.Tip() and it wasn't in
|
|
4417
|
-
// setBlockIndexCandidates, then it must be in m_blocks_unlinked.
|
|
4418
|
-
if (!CBlockIndexWorkComparator()(pindex, m_chain.Tip()) && setBlockIndexCandidates.count(pindex) == 0) {
|
|
4419
|
-
if (pindexFirstInvalid == nullptr) {
|
|
4420
|
-
assert(foundInUnlinked);
|
|
4421
|
-
}
|
|
4422
|
-
}
|
|
4423
|
-
}
|
|
4424
4361
|
// assert(pindex->GetBlockHash() == pindex->GetBlockHeader().GetHash()); // Perhaps too slow
|
|
4425
4362
|
// End: actual consistency checks.
|
|
4426
4363
|
|
|
@@ -4678,6 +4615,126 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin
|
|
|
4678
4615
|
return std::min<double>(pindex->nChainTx / fTxTotal, 1.0);
|
|
4679
4616
|
}
|
|
4680
4617
|
|
|
4618
|
+
|
|
4619
|
+
|
|
4620
|
+
|
|
4621
|
+
|
|
4622
|
+
// peercoin: total coin age spent in transaction, in the unit of coin-days.
|
|
4623
|
+
// Only those coins meeting minimum age requirement counts. As those
|
|
4624
|
+
// transactions not in main chain are not currently indexed so we
|
|
4625
|
+
// might not find out about their coin age. Older transactions are
|
|
4626
|
+
// guaranteed to be in main chain by sync-checkpoint. This rule is
|
|
4627
|
+
// introduced to help nodes establish a consistent view of the coin
|
|
4628
|
+
// age (trust score) of competing branches.
|
|
4629
|
+
bool GetCoinAge(const CTransaction& tx, const CCoinsViewCache &view, uint64_t& nCoinAge, unsigned int nTimeTx, bool isTrueCoinAge)
|
|
4630
|
+
{
|
|
4631
|
+
arith_uint256 bnCentSecond = 0; // coin age in the unit of cent-seconds
|
|
4632
|
+
nCoinAge = 0;
|
|
4633
|
+
|
|
4634
|
+
if (tx.IsCoinBase())
|
|
4635
|
+
return true;
|
|
4636
|
+
|
|
4637
|
+
// Transaction index is required to get to block header
|
|
4638
|
+
if (!g_txindex)
|
|
4639
|
+
return false; // Transaction index not available
|
|
4640
|
+
|
|
4641
|
+
for (const auto& txin : tx.vin)
|
|
4642
|
+
{
|
|
4643
|
+
// First try finding the previous transaction in database
|
|
4644
|
+
const COutPoint &prevout = txin.prevout;
|
|
4645
|
+
Coin coin;
|
|
4646
|
+
|
|
4647
|
+
if (isTrueCoinAge && !view.GetCoin(prevout, coin))
|
|
4648
|
+
continue; // previous transaction not in main chain
|
|
4649
|
+
if (nTimeTx < coin.nTime)
|
|
4650
|
+
return false; // Transaction timestamp violation
|
|
4651
|
+
|
|
4652
|
+
CDiskTxPos postx;
|
|
4653
|
+
CBlockHeader header;
|
|
4654
|
+
CTransactionRef txPrev;
|
|
4655
|
+
auto it = g_txindex->cachedTxs.find(prevout.hash);
|
|
4656
|
+
if (it != g_txindex->cachedTxs.end()) {
|
|
4657
|
+
header = it->second.first;
|
|
4658
|
+
txPrev = it->second.second;
|
|
4659
|
+
} else {
|
|
4660
|
+
if (g_txindex->FindTxPosition(prevout.hash, postx)) {
|
|
4661
|
+
CAutoFile file(OpenBlockFile(postx, true), SER_DISK, CLIENT_VERSION);
|
|
4662
|
+
try {
|
|
4663
|
+
file >> header;
|
|
4664
|
+
fseek(file.Get(), postx.nTxOffset, SEEK_CUR);
|
|
4665
|
+
file >> txPrev;
|
|
4666
|
+
} catch (std::exception &e) {
|
|
4667
|
+
return error("%s() : deserialize or I/O error in GetCoinAge()", __PRETTY_FUNCTION__);
|
|
4668
|
+
}
|
|
4669
|
+
} else
|
|
4670
|
+
return error("%s() : tx missing in tx index in GetCoinAge()", __PRETTY_FUNCTION__);
|
|
4671
|
+
g_txindex->cachedTxs[prevout.hash] = std::pair(header,txPrev);
|
|
4672
|
+
}
|
|
4673
|
+
|
|
4674
|
+
if (txPrev->GetHash() != prevout.hash)
|
|
4675
|
+
return error("%s() : txid mismatch in GetCoinAge()", __PRETTY_FUNCTION__);
|
|
4676
|
+
|
|
4677
|
+
if (header.GetBlockTime() + Params().GetConsensus().nStakeMinAge > nTimeTx)
|
|
4678
|
+
continue; // only count coins meeting min age requirement
|
|
4679
|
+
|
|
4680
|
+
int64_t nValueIn = txPrev->vout[txin.prevout.n].nValue;
|
|
4681
|
+
int nEffectiveAge = nTimeTx-(txPrev->nTime ? txPrev->nTime : header.GetBlockTime());
|
|
4682
|
+
|
|
4683
|
+
if (!isTrueCoinAge || IsProtocolV09(nTimeTx))
|
|
4684
|
+
nEffectiveAge = std::min(nEffectiveAge, 365 * 24 * 60 * 60);
|
|
4685
|
+
|
|
4686
|
+
bnCentSecond += arith_uint256(nValueIn) * nEffectiveAge / CENT;
|
|
4687
|
+
|
|
4688
|
+
if (gArgs.GetBoolArg("-printcoinage", false))
|
|
4689
|
+
LogPrintf("coin age nValueIn=%-12lld nTimeDiff=%d bnCentSecond=%s\n", nValueIn, nEffectiveAge, bnCentSecond.ToString());
|
|
4690
|
+
}
|
|
4691
|
+
|
|
4692
|
+
arith_uint256 bnCoinDay = bnCentSecond * CENT / COIN / (24 * 60 * 60);
|
|
4693
|
+
if (gArgs.GetBoolArg("-printcoinage", false))
|
|
4694
|
+
LogPrintf("coin age bnCoinDay=%s\n", bnCoinDay.ToString());
|
|
4695
|
+
nCoinAge = bnCoinDay.GetLow64();
|
|
4696
|
+
return true;
|
|
4697
|
+
}
|
|
4698
|
+
|
|
4699
|
+
// peercoin: sign block
|
|
4700
|
+
typedef std::vector<unsigned char> valtype;
|
|
4701
|
+
bool SignBlock(CBlock& block, const CWallet& keystore)
|
|
4702
|
+
{
|
|
4703
|
+
std::vector<valtype> vSolutions;
|
|
4704
|
+
const CTxOut& txout = block.IsProofOfStake()? block.vtx[1]->vout[1] : block.vtx[0]->vout[0];
|
|
4705
|
+
|
|
4706
|
+
if (Solver(txout.scriptPubKey, vSolutions) != TxoutType::PUBKEY)
|
|
4707
|
+
return false;
|
|
4708
|
+
|
|
4709
|
+
// Sign
|
|
4710
|
+
const valtype& vchPubKey = vSolutions[0];
|
|
4711
|
+
CKey key;
|
|
4712
|
+
if (!keystore.GetLegacyScriptPubKeyMan()->GetKey(CKeyID(Hash160(vchPubKey)), key))
|
|
4713
|
+
return false;
|
|
4714
|
+
if (key.GetPubKey() != CPubKey(vchPubKey))
|
|
4715
|
+
return false;
|
|
4716
|
+
return key.Sign(block.GetHash(), block.vchBlockSig, 0);
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4719
|
+
// peercoin: check block signature
|
|
4720
|
+
bool CheckBlockSignature(const CBlock& block)
|
|
4721
|
+
{
|
|
4722
|
+
if (block.GetHash() == Params().GetConsensus().hashGenesisBlock)
|
|
4723
|
+
return block.vchBlockSig.empty();
|
|
4724
|
+
|
|
4725
|
+
std::vector<valtype> vSolutions;
|
|
4726
|
+
const CTxOut& txout = block.IsProofOfStake()? block.vtx[1]->vout[1] : block.vtx[0]->vout[0];
|
|
4727
|
+
|
|
4728
|
+
if (Solver(txout.scriptPubKey, vSolutions) != TxoutType::PUBKEY)
|
|
4729
|
+
return false;
|
|
4730
|
+
|
|
4731
|
+
const valtype& vchPubKey = vSolutions[0];
|
|
4732
|
+
CPubKey key(vchPubKey);
|
|
4733
|
+
if (block.vchBlockSig.empty())
|
|
4734
|
+
return false;
|
|
4735
|
+
return key.Verify(block.GetHash(), block.vchBlockSig);
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4681
4738
|
std::optional<uint256> ChainstateManager::SnapshotBlockhash() const
|
|
4682
4739
|
{
|
|
4683
4740
|
LOCK(::cs_main);
|
|
@@ -5009,7 +5066,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot(
|
|
|
5009
5066
|
|
|
5010
5067
|
// Fake BLOCK_OPT_WITNESS so that CChainState::NeedsRedownload()
|
|
5011
5068
|
// won't ask to rewind the entire assumed-valid chain on startup.
|
|
5012
|
-
if (
|
|
5069
|
+
if (IsBTC16BIPsEnabled(index->nTime)) {
|
|
5013
5070
|
index->nStatus |= BLOCK_OPT_WITNESS;
|
|
5014
5071
|
}
|
|
5015
5072
|
|
|
@@ -16,16 +16,17 @@
|
|
|
16
16
|
#include <consensus/amount.h>
|
|
17
17
|
#include <fs.h>
|
|
18
18
|
#include <node/blockstorage.h>
|
|
19
|
-
#include <policy/feerate.h>
|
|
20
19
|
#include <policy/packages.h>
|
|
21
20
|
#include <script/script_error.h>
|
|
22
21
|
#include <sync.h>
|
|
22
|
+
#include <chain.h>
|
|
23
23
|
#include <txdb.h>
|
|
24
24
|
#include <txmempool.h> // For CTxMemPool::cs
|
|
25
25
|
#include <uint256.h>
|
|
26
26
|
#include <util/check.h>
|
|
27
27
|
#include <util/hasher.h>
|
|
28
28
|
#include <util/translation.h>
|
|
29
|
+
#include <wallet/wallet.h>
|
|
29
30
|
|
|
30
31
|
#include <atomic>
|
|
31
32
|
#include <map>
|
|
@@ -41,8 +42,14 @@
|
|
|
41
42
|
class CChainState;
|
|
42
43
|
class CBlockTreeDB;
|
|
43
44
|
class CChainParams;
|
|
45
|
+
namespace wallet {
|
|
46
|
+
class CWallet;
|
|
47
|
+
} // namespace wallet
|
|
48
|
+
//class CWallet;
|
|
49
|
+
using wallet::CWallet;
|
|
44
50
|
class CTxMemPool;
|
|
45
51
|
class ChainstateManager;
|
|
52
|
+
class CKeyStore;
|
|
46
53
|
struct ChainTxData;
|
|
47
54
|
struct DisconnectedBlockTransactions;
|
|
48
55
|
struct PrecomputedTransactionData;
|
|
@@ -52,8 +59,6 @@ namespace node {
|
|
|
52
59
|
class SnapshotMetadata;
|
|
53
60
|
} // namespace node
|
|
54
61
|
|
|
55
|
-
/** Default for -minrelaytxfee, minimum relay fee for transactions */
|
|
56
|
-
static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000;
|
|
57
62
|
/** Default for -limitancestorcount, max number of in-mempool ancestors */
|
|
58
63
|
static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25;
|
|
59
64
|
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */
|
|
@@ -80,7 +85,7 @@ static const int MAX_SCRIPTCHECK_THREADS = 15;
|
|
|
80
85
|
static const int DEFAULT_SCRIPTCHECK_THREADS = 0;
|
|
81
86
|
static const int64_t DEFAULT_MAX_TIP_AGE = 24 * 60 * 60;
|
|
82
87
|
static const bool DEFAULT_CHECKPOINTS_ENABLED = true;
|
|
83
|
-
static const bool DEFAULT_TXINDEX =
|
|
88
|
+
static const bool DEFAULT_TXINDEX = true; // peercoin: txindex is required for PoS calculations (might change in the future)
|
|
84
89
|
static constexpr bool DEFAULT_COINSTATSINDEX{false};
|
|
85
90
|
static const char* const DEFAULT_BLOCKFILTERINDEX = "0";
|
|
86
91
|
/** Default for -persistmempool */
|
|
@@ -120,8 +125,7 @@ extern bool g_parallel_script_checks;
|
|
|
120
125
|
extern bool fRequireStandard;
|
|
121
126
|
extern bool fCheckBlockIndex;
|
|
122
127
|
extern bool fCheckpointsEnabled;
|
|
123
|
-
|
|
124
|
-
extern CFeeRate minRelayTxFee;
|
|
128
|
+
extern bool fAlerts;
|
|
125
129
|
/** If the tip is older than this (in seconds), the node is considered to be in initial block download. */
|
|
126
130
|
extern int64_t nMaxTipAge;
|
|
127
131
|
|
|
@@ -134,9 +138,11 @@ extern arith_uint256 nMinimumChainWork;
|
|
|
134
138
|
/** Best header we've seen so far (used for getheaders queries' starting points). */
|
|
135
139
|
extern CBlockIndex *pindexBestHeader;
|
|
136
140
|
|
|
141
|
+
|
|
137
142
|
/** Documentation for argument 'checklevel'. */
|
|
138
143
|
extern const std::vector<std::string> CHECKLEVEL_DOC;
|
|
139
144
|
|
|
145
|
+
|
|
140
146
|
/** Unload database information */
|
|
141
147
|
void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman);
|
|
142
148
|
/** Run instances of script checking worker threads */
|
|
@@ -144,15 +150,12 @@ void StartScriptCheckWorkerThreads(int threads_num);
|
|
|
144
150
|
/** Stop all of the script checking worker threads */
|
|
145
151
|
void StopScriptCheckWorkerThreads();
|
|
146
152
|
|
|
147
|
-
CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams);
|
|
148
153
|
|
|
149
154
|
bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str{});
|
|
150
155
|
|
|
151
156
|
/** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */
|
|
152
157
|
double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex);
|
|
153
158
|
|
|
154
|
-
/** Prune block files up to a given height */
|
|
155
|
-
void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight);
|
|
156
159
|
|
|
157
160
|
/**
|
|
158
161
|
* Validation result for a single transaction mempool acceptance.
|
|
@@ -272,7 +275,6 @@ MempoolAcceptResult AcceptToMemoryPool(CChainState& active_chainstate, const CTr
|
|
|
272
275
|
PackageMempoolAcceptResult ProcessNewPackage(CChainState& active_chainstate, CTxMemPool& pool,
|
|
273
276
|
const Package& txns, bool test_accept)
|
|
274
277
|
EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
|
275
|
-
|
|
276
278
|
/** Transaction validation functions */
|
|
277
279
|
|
|
278
280
|
/**
|
|
@@ -351,7 +353,7 @@ void InitScriptExecutionCache();
|
|
|
351
353
|
/** Functions for validating blocks and updating the block tree */
|
|
352
354
|
|
|
353
355
|
/** Context-independent validity checks */
|
|
354
|
-
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
|
|
356
|
+
bool CheckBlock(const CBlock& block, BlockValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true, bool fCheckSignature = true);
|
|
355
357
|
|
|
356
358
|
/** Check a block is completely valid from start to finish (only works on top of our current best block) */
|
|
357
359
|
bool TestBlockValidity(BlockValidationState& state,
|
|
@@ -624,10 +626,6 @@ public:
|
|
|
624
626
|
//! Unconditionally flush all changes to disk.
|
|
625
627
|
void ForceFlushStateToDisk();
|
|
626
628
|
|
|
627
|
-
//! Prune blockfiles from the disk if necessary and then flush chainstate changes
|
|
628
|
-
//! if we pruned.
|
|
629
|
-
void PruneAndFlush();
|
|
630
|
-
|
|
631
629
|
/**
|
|
632
630
|
* Find the best known block, and make it the tip of the block chain. The
|
|
633
631
|
* result is either failure or an activated best chain. pblock is either
|
|
@@ -961,7 +959,7 @@ public:
|
|
|
961
959
|
* @param[out] new_block A boolean which is set to indicate if the block was first received via this call
|
|
962
960
|
* @returns If the block was processed, independently of block validity
|
|
963
961
|
*/
|
|
964
|
-
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block) LOCKS_EXCLUDED(cs_main);
|
|
962
|
+
bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<const CBlock>& block, bool force_processing, bool* new_block, CBlockIndex** ppindex = nullptr, bool* fPoSDuplicate = nullptr) LOCKS_EXCLUDED(cs_main);
|
|
965
963
|
|
|
966
964
|
/**
|
|
967
965
|
* Process incoming block headers.
|
|
@@ -974,7 +972,7 @@ public:
|
|
|
974
972
|
* @param[in] chainparams The params for the chain we want to connect to
|
|
975
973
|
* @param[out] ppindex If set, the pointer will be set to point to the last new block index object for the given headers
|
|
976
974
|
*/
|
|
977
|
-
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
|
|
975
|
+
bool ProcessNewBlockHeaders(int32_t& nPoSTemperature, const uint256& lastAcceptedHeader, const std::vector<CBlockHeader>& block, BlockValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr) LOCKS_EXCLUDED(cs_main);
|
|
978
976
|
|
|
979
977
|
/**
|
|
980
978
|
* Try to add a transaction to the memory pool.
|
|
@@ -1013,6 +1011,12 @@ bool DumpMempool(const CTxMemPool& pool, FopenFn mockable_fopen_function = fsbri
|
|
|
1013
1011
|
/** Load the mempool from disk. */
|
|
1014
1012
|
bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate, FopenFn mockable_fopen_function = fsbridge::fopen);
|
|
1015
1013
|
|
|
1014
|
+
// peercoin:
|
|
1015
|
+
CAmount GetProofOfWorkReward(unsigned int nBits, uint32_t nTime);
|
|
1016
|
+
CAmount GetProofOfStakeReward(int64_t nCoinAge, uint32_t nTime, uint64_t nMoneySupply);
|
|
1017
|
+
bool GetCoinAge(const CTransaction& tx, const CCoinsViewCache &view, uint64_t& nCoinAge, unsigned int nTimeTx, bool isTrueCoinAge = true); // peercoin: get transaction coin age
|
|
1018
|
+
bool SignBlock(CBlock& block, const CWallet& keystore);
|
|
1019
|
+
bool CheckBlockSignature(const CBlock& block);
|
|
1016
1020
|
/**
|
|
1017
1021
|
* Return the expected assumeutxo value for a given height, if one exists.
|
|
1018
1022
|
*
|
|
@@ -109,7 +109,6 @@ protected:
|
|
|
109
109
|
* - SIZELIMIT (removed in size limiting if the mempool exceeds -maxmempool megabytes)
|
|
110
110
|
* - REORG (removed during a reorg)
|
|
111
111
|
* - CONFLICT (removed because it conflicts with in-block transaction)
|
|
112
|
-
* - REPLACED (removed due to RBF replacement)
|
|
113
112
|
*
|
|
114
113
|
* This does not fire for transactions that are removed from the mempool
|
|
115
114
|
* because they have been included in a block. Any client that is interested
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
* network protocol versioning
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
static const int PROTOCOL_VERSION =
|
|
12
|
+
static const int PROTOCOL_VERSION = 70017;
|
|
13
|
+
static const int OLD_VERSION = 70014; // peercoin: used to communicate with clients that don't know how to send PoS information in headers
|
|
13
14
|
|
|
14
15
|
//! initial proto version, to be increased after version/verack negotiation
|
|
15
16
|
static const int INIT_PROTO_VERSION = 209;
|
|
16
17
|
|
|
17
18
|
//! disconnect from peers older than this proto version
|
|
18
|
-
static const int MIN_PEER_PROTO_VERSION =
|
|
19
|
+
static const int MIN_PEER_PROTO_VERSION = 70016;
|
|
19
20
|
|
|
20
21
|
//! BIP 0031, pong message, is enabled for all versions AFTER this one
|
|
21
22
|
static const int BIP0031_VERSION = 60000;
|
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2016-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#include <versionbits.h>
|
|
6
|
-
#include <consensus/params.h>
|
|
7
|
-
|
|
8
|
-
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
|
|
9
|
-
{
|
|
10
|
-
int nPeriod = Period(params);
|
|
11
|
-
int nThreshold = Threshold(params);
|
|
12
|
-
int min_activation_height = MinActivationHeight(params);
|
|
13
|
-
int64_t nTimeStart = BeginTime(params);
|
|
14
|
-
int64_t nTimeTimeout = EndTime(params);
|
|
15
|
-
|
|
16
|
-
// Check if this deployment is always active.
|
|
17
|
-
if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
|
|
18
|
-
return ThresholdState::ACTIVE;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Check if this deployment is never active.
|
|
22
|
-
if (nTimeStart == Consensus::BIP9Deployment::NEVER_ACTIVE) {
|
|
23
|
-
return ThresholdState::FAILED;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
|
|
27
|
-
if (pindexPrev != nullptr) {
|
|
28
|
-
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Walk backwards in steps of nPeriod to find a pindexPrev whose information is known
|
|
32
|
-
std::vector<const CBlockIndex*> vToCompute;
|
|
33
|
-
while (cache.count(pindexPrev) == 0) {
|
|
34
|
-
if (pindexPrev == nullptr) {
|
|
35
|
-
// The genesis block is by definition defined.
|
|
36
|
-
cache[pindexPrev] = ThresholdState::DEFINED;
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
if (pindexPrev->GetMedianTimePast() < nTimeStart) {
|
|
40
|
-
// Optimization: don't recompute down further, as we know every earlier block will be before the start time
|
|
41
|
-
cache[pindexPrev] = ThresholdState::DEFINED;
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
vToCompute.push_back(pindexPrev);
|
|
45
|
-
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// At this point, cache[pindexPrev] is known
|
|
49
|
-
assert(cache.count(pindexPrev));
|
|
50
|
-
ThresholdState state = cache[pindexPrev];
|
|
51
|
-
|
|
52
|
-
// Now walk forward and compute the state of descendants of pindexPrev
|
|
53
|
-
while (!vToCompute.empty()) {
|
|
54
|
-
ThresholdState stateNext = state;
|
|
55
|
-
pindexPrev = vToCompute.back();
|
|
56
|
-
vToCompute.pop_back();
|
|
57
|
-
|
|
58
|
-
switch (state) {
|
|
59
|
-
case ThresholdState::DEFINED: {
|
|
60
|
-
if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
|
|
61
|
-
stateNext = ThresholdState::STARTED;
|
|
62
|
-
}
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
case ThresholdState::STARTED: {
|
|
66
|
-
// We need to count
|
|
67
|
-
const CBlockIndex* pindexCount = pindexPrev;
|
|
68
|
-
int count = 0;
|
|
69
|
-
for (int i = 0; i < nPeriod; i++) {
|
|
70
|
-
if (Condition(pindexCount, params)) {
|
|
71
|
-
count++;
|
|
72
|
-
}
|
|
73
|
-
pindexCount = pindexCount->pprev;
|
|
74
|
-
}
|
|
75
|
-
if (count >= nThreshold) {
|
|
76
|
-
stateNext = ThresholdState::LOCKED_IN;
|
|
77
|
-
} else if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
|
|
78
|
-
stateNext = ThresholdState::FAILED;
|
|
79
|
-
}
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
case ThresholdState::LOCKED_IN: {
|
|
83
|
-
// Progresses into ACTIVE provided activation height will have been reached.
|
|
84
|
-
if (pindexPrev->nHeight + 1 >= min_activation_height) {
|
|
85
|
-
stateNext = ThresholdState::ACTIVE;
|
|
86
|
-
}
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
case ThresholdState::FAILED:
|
|
90
|
-
case ThresholdState::ACTIVE: {
|
|
91
|
-
// Nothing happens, these are terminal states.
|
|
92
|
-
break;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
cache[pindexPrev] = state = stateNext;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return state;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector<bool>* signalling_blocks) const
|
|
102
|
-
{
|
|
103
|
-
BIP9Stats stats = {};
|
|
104
|
-
|
|
105
|
-
stats.period = Period(params);
|
|
106
|
-
stats.threshold = Threshold(params);
|
|
107
|
-
|
|
108
|
-
if (pindex == nullptr) return stats;
|
|
109
|
-
|
|
110
|
-
// Find how many blocks are in the current period
|
|
111
|
-
int blocks_in_period = 1 + (pindex->nHeight % stats.period);
|
|
112
|
-
|
|
113
|
-
// Reset signalling_blocks
|
|
114
|
-
if (signalling_blocks) {
|
|
115
|
-
signalling_blocks->assign(blocks_in_period, false);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Count from current block to beginning of period
|
|
119
|
-
int elapsed = 0;
|
|
120
|
-
int count = 0;
|
|
121
|
-
const CBlockIndex* currentIndex = pindex;
|
|
122
|
-
do {
|
|
123
|
-
++elapsed;
|
|
124
|
-
--blocks_in_period;
|
|
125
|
-
if (Condition(currentIndex, params)) {
|
|
126
|
-
++count;
|
|
127
|
-
if (signalling_blocks) signalling_blocks->at(blocks_in_period) = true;
|
|
128
|
-
}
|
|
129
|
-
currentIndex = currentIndex->pprev;
|
|
130
|
-
} while(blocks_in_period > 0);
|
|
131
|
-
|
|
132
|
-
stats.elapsed = elapsed;
|
|
133
|
-
stats.count = count;
|
|
134
|
-
stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count);
|
|
135
|
-
|
|
136
|
-
return stats;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
|
|
140
|
-
{
|
|
141
|
-
int64_t start_time = BeginTime(params);
|
|
142
|
-
if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE || start_time == Consensus::BIP9Deployment::NEVER_ACTIVE) {
|
|
143
|
-
return 0;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const ThresholdState initialState = GetStateFor(pindexPrev, params, cache);
|
|
147
|
-
|
|
148
|
-
// BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment."
|
|
149
|
-
if (initialState == ThresholdState::DEFINED) {
|
|
150
|
-
return 0;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const int nPeriod = Period(params);
|
|
154
|
-
|
|
155
|
-
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1.
|
|
156
|
-
// To ease understanding of the following height calculation, it helps to remember that
|
|
157
|
-
// right now pindexPrev points to the block prior to the block that we are computing for, thus:
|
|
158
|
-
// if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and
|
|
159
|
-
// if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period.
|
|
160
|
-
// The parent of the genesis block is represented by nullptr.
|
|
161
|
-
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
|
|
162
|
-
|
|
163
|
-
const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
|
|
164
|
-
|
|
165
|
-
while (previousPeriodParent != nullptr && GetStateFor(previousPeriodParent, params, cache) == initialState) {
|
|
166
|
-
pindexPrev = previousPeriodParent;
|
|
167
|
-
previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Adjust the result because right now we point to the parent block.
|
|
171
|
-
return pindexPrev->nHeight + 1;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
namespace
|
|
175
|
-
{
|
|
176
|
-
/**
|
|
177
|
-
* Class to implement versionbits logic.
|
|
178
|
-
*/
|
|
179
|
-
class VersionBitsConditionChecker : public AbstractThresholdConditionChecker {
|
|
180
|
-
private:
|
|
181
|
-
const Consensus::DeploymentPos id;
|
|
182
|
-
|
|
183
|
-
protected:
|
|
184
|
-
int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; }
|
|
185
|
-
int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; }
|
|
186
|
-
int MinActivationHeight(const Consensus::Params& params) const override { return params.vDeployments[id].min_activation_height; }
|
|
187
|
-
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
|
|
188
|
-
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
|
|
189
|
-
|
|
190
|
-
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
|
|
191
|
-
{
|
|
192
|
-
return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
public:
|
|
196
|
-
explicit VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {}
|
|
197
|
-
uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; }
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
} // namespace
|
|
201
|
-
|
|
202
|
-
ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
|
|
203
|
-
{
|
|
204
|
-
LOCK(m_mutex);
|
|
205
|
-
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector<bool>* signalling_blocks)
|
|
209
|
-
{
|
|
210
|
-
return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindex, params, signalling_blocks);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
int VersionBitsCache::StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
|
|
214
|
-
{
|
|
215
|
-
LOCK(m_mutex);
|
|
216
|
-
return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, m_caches[pos]);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
uint32_t VersionBitsCache::Mask(const Consensus::Params& params, Consensus::DeploymentPos pos)
|
|
220
|
-
{
|
|
221
|
-
return VersionBitsConditionChecker(pos).Mask(params);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
int32_t VersionBitsCache::ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params)
|
|
225
|
-
{
|
|
226
|
-
LOCK(m_mutex);
|
|
227
|
-
int32_t nVersion = VERSIONBITS_TOP_BITS;
|
|
228
|
-
|
|
229
|
-
for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) {
|
|
230
|
-
Consensus::DeploymentPos pos = static_cast<Consensus::DeploymentPos>(i);
|
|
231
|
-
ThresholdState state = VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]);
|
|
232
|
-
if (state == ThresholdState::LOCKED_IN || state == ThresholdState::STARTED) {
|
|
233
|
-
nVersion |= Mask(params, pos);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return nVersion;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
void VersionBitsCache::Clear()
|
|
241
|
-
{
|
|
242
|
-
LOCK(m_mutex);
|
|
243
|
-
for (unsigned int d = 0; d < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; d++) {
|
|
244
|
-
m_caches[d].clear();
|
|
245
|
-
}
|
|
246
|
-
}
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2016-2021 The Bitcoin Core developers
|
|
2
|
-
// Distributed under the MIT software license, see the accompanying
|
|
3
|
-
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
4
|
-
|
|
5
|
-
#ifndef BITCOIN_VERSIONBITS_H
|
|
6
|
-
#define BITCOIN_VERSIONBITS_H
|
|
7
|
-
|
|
8
|
-
#include <chain.h>
|
|
9
|
-
#include <sync.h>
|
|
10
|
-
|
|
11
|
-
#include <map>
|
|
12
|
-
|
|
13
|
-
/** What block version to use for new blocks (pre versionbits) */
|
|
14
|
-
static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4;
|
|
15
|
-
/** What bits to set in version for versionbits blocks */
|
|
16
|
-
static const int32_t VERSIONBITS_TOP_BITS = 0x20000000UL;
|
|
17
|
-
/** What bitmask determines whether versionbits is in use */
|
|
18
|
-
static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL;
|
|
19
|
-
/** Total bits available for versionbits */
|
|
20
|
-
static const int32_t VERSIONBITS_NUM_BITS = 29;
|
|
21
|
-
|
|
22
|
-
/** BIP 9 defines a finite-state-machine to deploy a softfork in multiple stages.
|
|
23
|
-
* State transitions happen during retarget period if conditions are met
|
|
24
|
-
* In case of reorg, transitions can go backward. Without transition, state is
|
|
25
|
-
* inherited between periods. All blocks of a period share the same state.
|
|
26
|
-
*/
|
|
27
|
-
enum class ThresholdState {
|
|
28
|
-
DEFINED, // First state that each softfork starts out as. The genesis block is by definition in this state for each deployment.
|
|
29
|
-
STARTED, // For blocks past the starttime.
|
|
30
|
-
LOCKED_IN, // For at least one retarget period after the first retarget period with STARTED blocks of which at least threshold have the associated bit set in nVersion, until min_activation_height is reached.
|
|
31
|
-
ACTIVE, // For all blocks after the LOCKED_IN retarget period (final state)
|
|
32
|
-
FAILED, // For all blocks once the first retarget period after the timeout time is hit, if LOCKED_IN wasn't already reached (final state)
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
// A map that gives the state for blocks whose height is a multiple of Period().
|
|
36
|
-
// The map is indexed by the block's parent, however, so all keys in the map
|
|
37
|
-
// will either be nullptr or a block with (height + 1) % Period() == 0.
|
|
38
|
-
typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
|
|
39
|
-
|
|
40
|
-
/** Display status of an in-progress BIP9 softfork */
|
|
41
|
-
struct BIP9Stats {
|
|
42
|
-
/** Length of blocks of the BIP9 signalling period */
|
|
43
|
-
int period;
|
|
44
|
-
/** Number of blocks with the version bit set required to activate the softfork */
|
|
45
|
-
int threshold;
|
|
46
|
-
/** Number of blocks elapsed since the beginning of the current period */
|
|
47
|
-
int elapsed;
|
|
48
|
-
/** Number of blocks with the version bit set since the beginning of the current period */
|
|
49
|
-
int count;
|
|
50
|
-
/** False if there are not enough blocks left in this period to pass activation threshold */
|
|
51
|
-
bool possible;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Abstract class that implements BIP9-style threshold logic, and caches results.
|
|
56
|
-
*/
|
|
57
|
-
class AbstractThresholdConditionChecker {
|
|
58
|
-
protected:
|
|
59
|
-
virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0;
|
|
60
|
-
virtual int64_t BeginTime(const Consensus::Params& params) const =0;
|
|
61
|
-
virtual int64_t EndTime(const Consensus::Params& params) const =0;
|
|
62
|
-
virtual int MinActivationHeight(const Consensus::Params& params) const { return 0; }
|
|
63
|
-
virtual int Period(const Consensus::Params& params) const =0;
|
|
64
|
-
virtual int Threshold(const Consensus::Params& params) const =0;
|
|
65
|
-
|
|
66
|
-
public:
|
|
67
|
-
/** Returns the numerical statistics of an in-progress BIP9 softfork in the period including pindex
|
|
68
|
-
* If provided, signalling_blocks is set to true/false based on whether each block in the period signalled
|
|
69
|
-
*/
|
|
70
|
-
BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector<bool>* signalling_blocks = nullptr) const;
|
|
71
|
-
/** Returns the state for pindex A based on parent pindexPrev B. Applies any state transition if conditions are present.
|
|
72
|
-
* Caches state from first block of period. */
|
|
73
|
-
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
|
|
74
|
-
/** Returns the height since when the ThresholdState has started for pindex A based on parent pindexPrev B, all blocks of a period share the same */
|
|
75
|
-
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
/** BIP 9 allows multiple softforks to be deployed in parallel. We cache
|
|
79
|
-
* per-period state for every one of them. */
|
|
80
|
-
class VersionBitsCache
|
|
81
|
-
{
|
|
82
|
-
private:
|
|
83
|
-
Mutex m_mutex;
|
|
84
|
-
ThresholdConditionCache m_caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] GUARDED_BY(m_mutex);
|
|
85
|
-
|
|
86
|
-
public:
|
|
87
|
-
/** Get the numerical statistics for a given deployment for the signalling period that includes pindex.
|
|
88
|
-
* If provided, signalling_blocks is set to true/false based on whether each block in the period signalled
|
|
89
|
-
*/
|
|
90
|
-
static BIP9Stats Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector<bool>* signalling_blocks = nullptr);
|
|
91
|
-
|
|
92
|
-
static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos);
|
|
93
|
-
|
|
94
|
-
/** Get the BIP9 state for a given deployment for the block after pindexPrev. */
|
|
95
|
-
ThresholdState State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
|
|
96
|
-
|
|
97
|
-
/** Get the block height at which the BIP9 deployment switched into the state for the block after pindexPrev. */
|
|
98
|
-
int StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
|
|
99
|
-
|
|
100
|
-
/** Determine what nVersion a new block should use
|
|
101
|
-
*/
|
|
102
|
-
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params);
|
|
103
|
-
|
|
104
|
-
void Clear();
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
#endif // BITCOIN_VERSIONBITS_H
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
static Mutex g_warnings_mutex;
|
|
16
16
|
static bilingual_str g_misc_warnings GUARDED_BY(g_warnings_mutex);
|
|
17
17
|
static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false;
|
|
18
|
+
std::string strMintWarning;
|
|
18
19
|
|
|
19
20
|
void SetMiscWarning(const bilingual_str& warning)
|
|
20
21
|
{
|
|
@@ -41,6 +42,13 @@ bilingual_str GetWarnings(bool verbose)
|
|
|
41
42
|
warnings_verbose.emplace_back(warnings_concise);
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
// peercoin: wallet lock warning for minting
|
|
46
|
+
if (strMintWarning != "")
|
|
47
|
+
{
|
|
48
|
+
warnings_concise = Untranslated(strMintWarning);
|
|
49
|
+
warnings_verbose.emplace_back(warnings_concise);
|
|
50
|
+
}
|
|
51
|
+
|
|
44
52
|
// Misc warnings like out of disk space and clock is wrong
|
|
45
53
|
if (!g_misc_warnings.empty()) {
|
|
46
54
|
warnings_concise = g_misc_warnings;
|
|
@@ -20,4 +20,5 @@ void SetfLargeWorkInvalidChainFound(bool flag);
|
|
|
20
20
|
*/
|
|
21
21
|
bilingual_str GetWarnings(bool verbose);
|
|
22
22
|
|
|
23
|
+
extern std::string strMintWarning;
|
|
23
24
|
#endif // BITCOIN_WARNINGS_H
|